Bezkształtny: Rodzajowy.Aux

Próbuję zrozumieć, jak działa Generic (i TypeClass też). Wiki na github jest bardzo uboga w przykłady i dokumentację. Czy istnieje kanoniczny wpis na blogu / strona dokumentacji opisująca Generic i TypeClass szczegółowo?

W betonie, jaka jest różnica między tymi dwoma metodami?:

def find1[T](implicit gen: Generic[T]): Generic[T] = gen
def find2[T](implicit gen: Generic[T]): Generic[T] { type Repr = gen.Repr } = gen

Podane

object Generic {
  type Aux[T, Repr0] = Generic[T] { type Repr = Repr0 }
  def apply[T](implicit gen: Generic[T]): Aux[T, gen.Repr] = gen
  implicit def materialize[T, R]: Aux[T, R] = macro GenericMacros.materialize[T, R]
}
Author: Juanpa, 2015-11-16

1 answers

Kwestie związane z tym, jak Generic i TypeClass są realizowane i co robią, są na tyle różne, że prawdopodobnie zasługują na osobne pytania, więc zostanę przy Generic tutaj.

Generic zapewnia mapowanie z klas przypadków (i potencjalnie podobnych typów) do list heterogenicznych. Każda klasa case ma unikalną reprezentację hlist, ale każda dana klasa hlist odpowiada bardzo, bardzo dużej liczbie potencjalnych klas case. Na przykład, jeśli mamy następujący przypadek klasy:

case class Foo(i: Int, s: String)
case class Bar(x: Int, y: String)

Reprezentacja hlist dostarczona przez Generic dla Foo i Bar jest Int :: String :: HNil, która jest również reprezentacją dla (Int, String) i innych klas przypadków, które możemy zdefiniować za pomocą tych dwóch typów w tej kolejności.

(na marginesie, LabelledGeneric pozwala nam odróżnić Foo i Bar, ponieważ zawiera nazwy członków w reprezentacji jako ciągi znaków na poziomie typu.)

Ogólnie chcemy być w stanie określić klasę case I niech (unikalna) reprezentacja generyczna i uczynienie Repr członkiem typu (zamiast parametru typu) pozwala nam to zrobić całkiem czysto. Jeśli Typ reprezentacji hlist jest parametrem typu, to twoje metody find muszą mieć również parametr typu Repr, co oznacza, że nie będziesz mógł podać tylko T i wywnioskować Repr.

Tworzenie Repr członka typu ma sens tylko dlatego, że {[15] } jest jednoznacznie określony przez pierwszy parametr typu. Wyobraź sobie klasę typu Iso[A, B], która świadczy o tym, że A i B są izomorficzne. Ta klasa typu jest bardzo podobna do Generic, ale A nie wyróżnia się B-nie możemy po prostu zapytać "czym jest Typ, który jest izomorficzny do A?"- więc nie byłoby przydatne, aby B członek typu (chociaż moglibyśmy, gdybyśmy naprawdę chcieli - Iso[A] tylko tak naprawdę nic nie znaczą).

Problem z członkami typu polega na tym, że łatwo o nich zapomnieć, a gdy już ich nie ma, są odszedł na zawsze. Fakt, że zwracany typ find1 nie jest dopracowany (tzn. nie zawiera elementu type) oznacza, że zwracana instancja Generic jest praktycznie bezużyteczna. Na przykład statyczny typ res0 tutaj równie dobrze może być Any:

scala> import shapeless._
import shapeless._

scala> def find1[T](implicit gen: Generic[T]): Generic[T] = gen
find1: [T](implicit gen: shapeless.Generic[T])shapeless.Generic[T]

scala> case class Foo(i: Int, s: String)
defined class Foo

scala> find1[Foo].to(Foo(1, "ABC"))
res0: shapeless.Generic[Foo]#Repr = 1 :: ABC :: HNil

scala> res0.head
<console>:15: error: value head is not a member of shapeless.Generic[Foo]#Repr
              res0.head
                   ^

Gdy makro Generic.materialize shapeless tworzy instancję Generic[Foo], o którą prosimy, jest ona wpisywana statycznie jako Generic[Foo] { type Repr = Int :: String :: HNil }, więc argument gen, który kompilator przekazuje find1 zawiera wszystkie potrzebne nam informacje statyczne. Problem w tym, że my następnie jawnie up-cast ten typ do zwykłego starego nierafinowane Generic[Foo], i od tego momentu kompilator nie wie, co Repr jest dla tej instancji.

Typy Scali zależne od ścieżki dają nam sposób, aby nie zapomnieć o udoskonaleniu bez dodawania innego parametru typu do naszej metody. W twoim find2 kompilator statycznie zna Repr dla przychodzących gen, więc kiedy powiesz, że typem zwracanym jest Generic[T] { type Repr = gen.Repr }, będzie w stanie śledzić to informacje:

scala> find2[Foo].to(Foo(1, "ABC"))
res2: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 1 :: ABC :: HNil

scala> res2.head
res3: Int = 1

Podsumowując: Generic posiada parametr type T, który jednoznacznie określa jego typ Repr, Repr jest członkiem type zamiast parametru type, dzięki czemu nie musimy umieszczać go we wszystkich naszych podpisach type, A typy zależne od ścieżki umożliwiają to, pozwalając nam śledzić Repr, nawet jeśli nie ma go w naszych podpisach type.

 48
Author: Travis Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-16 15:27:07