Skip to content

Instantly share code, notes, and snippets.

@pierangeloc
Created June 14, 2015 20:59
Show Gist options
  • Save pierangeloc/9fc35e95e64138300d31 to your computer and use it in GitHub Desktop.
Save pierangeloc/9fc35e95e64138300d31 to your computer and use it in GitHub Desktop.

##Shapeless

Type class derivation: applies to ADT(Alg Data Types). ADT is a sealed _empty_trait extended by case classes. Many advantages

Type class: trait indexed on a type T and provides some methods and defs on the type T. in the comp obj we define a def of the typed class for that type, and we can derive it through the implicitly

trait Eq[T] {
	def eqv(x: T, y: T): Boolean
}

trait Animal...
object Animal {
	implicit def eqAnimal: Eq[Animal]...
}

def foo[T](x: T, y: T)(implicit eqt: Eq[T])  {
eqt.eqv(x, y)
}

and the implicit class to provide operators:

implicit class EqOps[T](x: T)(implicit eqt: Eq[T]) {
	def ===(y: T): Boolean = eqT.eqv(x, y)
}

How compiler looks for implicits: implicit[T]: look for immediate scope, and if not look on the companion objects of any type mentioned in the type. e.g. implicit Foo[Bar] ==> looks for implicit defs in the c.o. or Foo and in the c.o. of Bar

we defined the === in terms of any equivalence of any type that might be used in the ADT definition

we want the generation of the implicit type classes to be generated somehow by compiler

##about ADT Sum/product definition of ADT. We want to exploit this definition to have everything inferred by the compiler, we want a generic definition without cats and dogs

##Shapeless

scala> val l = 23 :: "foo" :: true :: HNil
l: shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.HNil]]] = 23 :: foo :: true :: HNil

hetherogeneous list: it preserves the type of components. See Ammonite repl to see how it can be represented the infix way

When we take the head, we know at compile time what is the type of that head Tree[T] :: Tree[T] :: HNil defines a product type of node!! T :: HNil defines a product type of the leaf Leaf[T] :+: Node[T] :+: CNil defines the sum type of the tree itself

-xloadImplicits ??? in the compiler to get information about implicit resolution.

Sum types: CNil: it is kind of nothing, it is not a nil, there is nothing at all. See Coproduct type in shapeless So if we have 2 animals being eq, animal can be either Cat or Dog

Shapeless' Generic provides a way to transform any case class to the equivalent HList, and go backwards

val gen = Generic[Cat]
gen.to(instanceOfCat) //---> HList representation of the Cat

Generic[T] provides way to go from/to represenation vs of a ADT to an equivalent HList or Coproduct. Using this we can define a generic equivalence method on any ADT. A Coproduct can be either the first alternative (Inl), or any other alternative (Inr) among the ones available in the Coproduct definition. In case of a to used on a Product type the representation is an HList, in case of a Coproduct type the representation is a Coproduct, that can be then resolved in an Inl (equivalent to a "first alternative of the coproduct") or Inr (equivalent to "something else in the coproduct found"). Watch out for the toString of Inl and Inr that hide the fact that we are talking about a coproduct, therefore int he REPL you might think that genAnimal.to(felix) returns a Cat, but instead it returns an Inl[Cat, ...]

scala> val genCat = Generic[Cat]
gen: shapeless.Generic[derivation.equal.Cat]{type Repr = shapeless.::[String,shapeless.::[Int,shapeless.HNil]]} = fresh$macro$3$1@1ee89f2

scala>  val felix2 = Cat("Felix", 2)
felix2: derivation.equal.Cat = Cat(Felix,2)

scala> genCat.to(felix2)
res0: gen.Repr = Felix :: 2 :: HNil

scala> res0.head
res1: String = Felix

scala> res0.head :: (res0.tail.head + 1) :: HNil
res2: shapeless.::[String,shapeless.::[Int,shapeless.HNil]] = Felix :: 3 :: HNil

scala> genCat.from(res2)
res3: derivation.equal.Cat = Cat(Felix,3)
//and make a Cat to a Dog
scala> val genDog = Generic[Dog]
genDog: shapeless.Generic[derivation.equal.Dog]{type Repr = shapeless.::[String,shapeless.::[Int,shapeless.HNil]]} = fresh$macro$6$1@50b93e10

scala> genDog.from(genCat.to(felix2))
res4: derivation.equal.Dog = Dog(Felix,2)

scala> val genAnimal = Generic[Animal]
genAnimal: shapeless.Generic[derivation.equal.Animal]{type Repr = shapeless.:+:[derivation.equal.Cat,shapeless.:+:[derivation.equal.Dog,shapeless.CNil]]} = fresh$macro$9$1@6c616408

scala> genAnimal.to(felix)
res13: genAnimal.Repr = Cat(Felix,5)

//watch out: previous expression din't return a Cat, but an Inl[Cat]
scala> val Inl(c) = genAnimal.to(felix)
c: derivation.equal.Cat = Cat(Felix,5)

scala> val Inl(c) = genAnimal.to(tigger)
scala.MatchError: Dog(Tigger,2) (of class shapeless.Inr)
  ... 35 elided

scala> val Inr(c) = genAnimal.to(tigger)
c: shapeless.:+:[derivation.equal.Dog,shapeless.CNil] = Dog(Tigger,2)

The Generic object type is aware of the type it is representing. In the to() part the representation is unaware of the type. If we get the Generic of a top level, like Animal, we get a Coproduct, or a Sum type:

val genA = Generic[Animal]

genA.to(felix)

we use Gen.Aux that lifts the type parameter as a type member in the class

Question: Why the base trait must be sealed? Because otherwise the compiler cannot resolve implicitly[Eq[Unsealed]]

##Applications Serialization/Codecs. Anything that comes naturally from the structure of they types, can be done through these generic programming structures.

why Lazy[]?

implicit def foo(implicit foo2: Foo):Foo = foo2 this implicit resolution would diverge, compiler boom and gives up With recursive types like Tree we have this problems finding the implicit to be applied, therefore we must work around this problem. Using Lazy[] we produce a dictionary (variable) that exists only at compile time. Advice: wrap into Lazy any type that we might figure having an implicitly resolution loop. But we promise the compiler that the loop will end at some point. If we don't keep the promise, loop @ compile time.

Functor: anything with a map is a functor.

Golden rule: put always implicit return value type!

see https://github.com/alexarchambault/scalacheck-shapeless/blob/master/src/main/scala/org/scalacheck/Shapeless.scala

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment