Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisalbright/7f7bfb12baecb1c876d8879fe5d21f92 to your computer and use it in GitHub Desktop.
Save chrisalbright/7f7bfb12baecb1c876d8879fe5d21f92 to your computer and use it in GitHub Desktop.
Examples with Shapeless 2.0, easier to understand from examples. Taken from docs, more examples/comments added etc
// Coproduct is extension of Either concept, to N multually exlusive choices
type ISB = Int :+: String :+: Boolean :+: CNil
val isb = Coproduct[ISB]("foo") //> isb : qaaz.ISB = foo
isb.select[Int] //> res0: Option[Int] = None
isb.select[String] //> res1: Option[String] = Some(foo)
object size extends Poly1 {
implicit def caseInt = at[Int](i => (i, i))
implicit def caseString = at[String](s => (s, s.length))
implicit def caseBoolean = at[Boolean](b => (b, 1))
}
val res2=isb map size //> res2 : shapeless.:+:[(Int, Int),shapeless.:+:[(String, Int),shapeless.:+:[(
//| Boolean, Int),shapeless.CNil]]] = (foo,3)
res2.select[(String, Int)] //> res2: Option[(String, Int)] = Some((foo,3))
import record.RecordType, syntax.singleton._, union._
// make a schema LIKE this, i.e. an examplar
val uSchema = RecordType.like('i ->> 23 :: 's ->> "foo" :: 'b ->> true :: HNil)
type U = uSchema.Union
val u = Coproduct[U]('s ->> "woof") // Inject a String into the union at label 's
//u: U = foo
u.get('i) // Nothing at 'i
//res0: Option[Int] = None
u.get('s) // Something at 's
//res1: Option[String] = Some(woof)
u.get('b) // Nothing at 'b
//res2: Option[Boolean] = None
// Would not compile
// println(u.get('x))
{
val uSchema2 = RecordType.like('i ->> 23 :: 'i2 ->> 100 :: HNil)
type U = uSchema2.Union
val u = Coproduct[U]('i2 ->> 1000)
println(u.get('i))
//None
println(u.get('i2))
//Some(1000)
}
/**
* Generic[T], where T is a case class or an abstract type at the root of a case class hierarchy,
* maps between values of T and a generic sum of products representation (HLists and Coproducts)
**/
case class Foo(i: Int, s: String, b: Boolean)
val fooGen = Generic[Foo] //> fooGen : shapeless.Generic[qaaz.Foo]{type Repr = shapeless.::[Int,shapeless
//| .::[String,shapeless.::[Boolean,shapeless.HNil]]]} = qaaz$$anonfun$main$1$fr
//| esh$macro$45$1@2b05039f
val foo = Foo(23, "foo", true) //> foo : qaaz.Foo = Foo(23,foo,true)
val hlist=fooGen.to(foo) //> hlist : qaaz.fooGen.Repr = 23 :: foo :: true :: HNil
val hlist2=13 :: hlist.tail //> hlist2 : shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeles
//| s.HNil]]] = 13 :: foo :: true :: HNil
fooGen.from(hlist2) //> res0: qaaz.Foo = Foo(13,foo,true)
// Simple recursive case class family
sealed trait Tree[T]
case class Leaf[T](t: T) extends Tree[T]
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T]
import poly._
// Polymorphic function which adds 1 to any Int and is the identity
// on all other values
object inc extends ->((i: Int) => i+1)
val tree: Tree[Int] =
Node(
Node(
Node(
Leaf(1),
Node(
Leaf(2),
Leaf(3)
)
),
Leaf(4)
),
Node(
Leaf(5),
Leaf(6)
)
) //> tree : qaaz.Tree[Int] = Node(Node(Node(Leaf(1),Node(Leaf(2),Leaf(3))),Leaf(
//| 4)),Node(Leaf(5),Leaf(6)))
// Transform tree by applying inc everywhere
val out=everywhere(inc)(tree) //> out : qaaz.Tree[Int] = Node(Node(Node(Leaf(2),Node(Leaf(3),Leaf(4))),Leaf(5
//| )),Node(Leaf(6),Leaf(7)))
// LabelledGeneric
import record._, syntax.singleton._
case class Book(author: String, title: String, id: Int, price: Double)
val bookGen = LabelledGeneric[Book]
val tapl = Book("Benjamin Pierce", "Types and Programming Languages", 262162091, 44.11)
val rec = bookGen.to(tapl) // Convert case class value to generic representation
//rec: bookGen.Repr = Benjamin Pierce :: Types and Programming Languages :: 262162091 :: 44.11 :: HNil
rec('price) // Access the price field symbolically, maintaining type information
//res0: Double = 44.11
bookGen.from(rec.updateWith('price)(_+2.0)) // type safe operations on fields
//res1: Book = Book(Benjamin Pierce,Types and Programming Languages,262162091,46.11)
case class ExtendedBook(author: String, title: String, id: Int, price: Double, inPrint: Boolean)
val bookExtGen = LabelledGeneric[ExtendedBook]
val extendedCase=bookExtGen.from(rec + ('inPrint ->> true)) // map values between case classes via generic representation
//extendedCase: ExtendedBook = ExtendedBook(Benjamin Pierce,Types and Programming Languages,262162091,44.11,true)
import shapeless._
import poly._
import syntax.zipper._
object choose extends (Set ~> Option) {
def apply[T](s : Set[T]) = s.headOption
}
val sets = Set(1) :: Set("foo") :: HNil //> sets : shapeless.::[scala.collection.immutable.Set[Int],shapeless.::[scala.
//| collection.immutable.Set[String],shapeless.HNil]] = Set(1) :: Set(foo) :: HN
//| il
val opts = sets map choose // map selects cases of choose for each HList element
//> opts : shapeless.::[Option[Int],shapeless.::[Option[String],shapeless.HNil]
//| ] = Some(1) :: Some(foo) :: HNil
val l = (23 :: "foo" :: HNil) :: HNil :: (true :: HNil) :: HNil
//> l : shapeless.::[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],shap
//| eless.::[shapeless.HNil.type,shapeless.::[shapeless.::[Boolean,shapeless.HNi
//| l],shapeless.HNil]]] = 23 :: foo :: HNil :: HNil :: true :: HNil :: HNil
(l flatMap identity) //> res0: shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.HN
//| il]]] = 23 :: foo :: true :: HNil
object size extends Poly1 {
implicit def caseInt = at[Int](x => 1)
implicit def caseString = at[String](_.length)
implicit def caseTuple[T, U]
(implicit st : Case.Aux[T, Int], su : Case.Aux[U, Int]) =
at[(T, U)](t => size(t._1)+size(t._2))
}
object addSize extends Poly2 {
implicit def default[T](implicit st: size.Case.Aux[T, Int]) =
at[Int, T]{ (acc, t) => acc+size(t) }
}
val l2 = 23 :: "foo" :: (13, "wibble") :: HNil //> l2 : shapeless.::[Int,shapeless.::[String,shapeless.::[(Int, String),shapel
//| ess.HNil]]] = 23 :: foo :: (13,wibble) :: HNil
println(l2.foldLeft(0)(addSize)) //> 11
// 11
val l3 = 1 :: "foo" :: 3.0 :: HNil //> l3 : shapeless.::[Int,shapeless.::[String,shapeless.::[Double,shapeless.HNi
//| l]]] = 1 :: foo :: 3.0 :: HNil
val zipper=l3.toZipper //> zipper : shapeless.Zipper[shapeless.::[Int,shapeless.::[String,shapeless.::
//| [Double,shapeless.HNil]]],shapeless.HNil,this.Repr,None.type] = Zipper(HNil,
//| 1 :: foo :: 3.0 :: HNil,None)
// a zipper is like a functional cursor. reify call makes it real, i.e. no longer a zipper
zipper.insert("A").right.insert("B").right.insert("C").right.insert("D").reify
//> res1: shapeless.::[String,shapeless.::[Int,shapeless.::[String,shapeless.::
//| [String,shapeless.::[String,shapeless.::[Double,shapeless.::[String,shapele
//| ss.HNil]]]]]]] = A :: 1 :: B :: foo :: C :: 3.0 :: D :: HNil
zipper.right.put(("wibble", 45)).reify //> 1 :: (wibble,45) :: 3.0 :: HNil
// right moves cursor to foo, put overwrites
zipper.right.delete.reify //> 1 :: 3.0 :: HNil
zipper.last.left.insert("bar").reify //> 1 :: foo :: bar :: 3.0 :: HNil
// index into HList
val l9 = 23 :: "foo" :: true :: HNil //> l : shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.HNi
//| l]]] = 23 :: foo :: true :: HNil
l9(1) //> res0: String = foo
import shapeless._
import poly._
trait Fruit
case class Apple() extends Fruit
case class Pear() extends Fruit
type FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNil
type APAP = Apple :: Pear :: Apple :: Pear :: HNil
val a : Apple = Apple() //> a : yyy.Apple = Apple()
val p : Pear = Pear() //> p : yyy.Pear = Pear()
val apap : APAP = a :: p :: a :: p :: HNil //> apap : yyy.APAP = Apple() :: Pear() :: Apple() :: Pear() :: HNil
val ffff : FFFF = apap //> ffff : yyy.FFFF = Apple() :: Pear() :: Apple() :: Pear() :: HNil
// APAP <: FFFF i.e. covariant
// Will not compile because of apple where pear expected
// val badapple:APAP=a :: p :: a :: a ::HNil
apap.unify //> res0: shapeless.::[Product with Serializable with yyy.Fruit,shapeless.::[Pro
//| duct with Serializable with yyy.Fruit,shapeless.::[Product with Serializable
//| with yyy.Fruit,shapeless.::[Product with Serializable with yyy.Fruit,shapel
//| ess.HNil]]]] = Apple() :: Pear() :: Apple() :: Pear() :: HNil
// note unify emits HList with least upper bound, i.e. Fruit
apap.toList //> res1: List[Product with Serializable with yyy.Fruit] = List(Apple(), Pear(),
//| Apple(), Pear())
import syntax.typeable._
val precise = ffff.cast[APAP] //> precise : Option[yyy.APAP] = Some(Apple() :: Pear() :: Apple() :: Pear() ::
//| HNil)
val pppp=p :: p :: p :: p ::HNil //> pppp : shapeless.::[yyy.Pear,shapeless.::[yyy.Pear,shapeless.::[yyy.Pear,sh
//| apeless.::[yyy.Pear,shapeless.HNil]]]] = Pear() :: Pear() :: Pear() :: Pear(
//| ) :: HNil
val precise2 = pppp.cast[APAP] //> precise2 : Option[yyy.APAP] = None
// as you can see, this fails with a None. The wonders of Option
//Heterogenous maps
// Key/value relation to be enforced: Strings map to Ints and vice versa
class BiMapIS[K, V]
implicit val intToString = new BiMapIS[Int, String]
//> intToString : qaaz.BiMapIS[Int,String] = qaaz$BiMapIS@66cd51c3
implicit val stringToInt = new BiMapIS[String, Int]
//> stringToInt : qaaz.BiMapIS[String,Int] = qaaz$BiMapIS@2d6e8792
val hm = HMap[BiMapIS](23 -> "foo", "bar" -> 13) //> hm : shapeless.HMap[qaaz.BiMapIS] = shapeless.HMap@13a57a3b
//val hm2 = HMap[BiMapIS](23 -> "foo", 23 -> 13) // Does not compile
hm.get(23) //> res0: Option[String] = Some(foo)
hm.get("bar") //> res1: Option[Int] = Some(13)
import hm._
val l = 23 :: "bar" :: HNil //> l : shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 23 :: bar :: HN
//| il
l map hm //> res2: shapeless.::[String,shapeless.::[Int,shapeless.HNil]] = foo :: 13 :: H
//| Nil
// A pair of ordinary case classes ...
case class Address(street : String, city : String, postcode : String)
case class Person(name : String, age : Int, address : Address)
// Some lenses over Person/Address ...
val nameLens = lens[Person] >> 'name
val ageLens = lens[Person] >> 'age
val addressLens = lens[Person] >> 'address
val streetLens = lens[Person] >> 'address >> 'street
val cityLens = lens[Person] >> 'address >> 'city
val postcodeLens = lens[Person] >> 'address >> 'postcode
val person = Person("Joe Grey", 37, Address("Southover Street", "Brighton", "BN2 9UA"))
//person: Person = Person(Joe Grey,37,Address(Southover Street,Brighton,BN2 9UA))
val age1 = ageLens.get(person) // Read field, note inferred type
//age1: Int = 37
val person2 = ageLens.set(person)(38) // Update field
//person2: Person = Person(Joe Grey,38,Address(Southover Street,Brighton,BN2 9UA))
val person3 = ageLens.modify(person2)(_ + 1) // Transform field
//person3: Person = Person(Joe Grey,39,Address(Southover Street,Brighton,BN2 9UA))
val street = streetLens.get(person3) // Read nested field
//street: String = Southover Street
val person4 = streetLens.set(person3)("Montpelier Road") // Update nested field
//person4: Person = Person(Joe Grey,39,Address(Montpelier Road,Brighton,BN2 9UA))
import shapeless._
import poly._
// choose is a function from Sets to Options with no type specific cases, i.e. polymorphic
object choose extends (Set ~> Option) {
def apply[T](s : Set[T]) = s.headOption
}
def choose2[T](s:Set[T])=s.headOption //> choose2: [T](s: Set[T])Option[T]
choose(Set(1, 2, 3)) //> res0: Option[Int] = Some(1)
choose(Set('a', 'b', 'c')) //> res1: Option[Char] = Some(a)
choose2(Set(1, 2, 3)) //> res2: Option[Int] = Some(1)
choose2(Set('a', 'b', 'c')) //> res3: Option[Char] = Some(a)
// So far looks the same
def pairApply(f: Set ~> Option) = (f(Set(1, 2, 3)), f(Set('a', 'b', 'c')))
//> pairApply: (f: shapeless.poly.~>[Set,Option])(Option[Int], Option[Char])
pairApply(choose) //> res4: (Option[Int], Option[Char]) = (Some(1),Some(a))
// pairApply2 can't take T, will barf. because not polymorphic. T can't be Int and Char at same time
def pairApply2[_](f: Set[_] => Option[_]) = (f(Set(1, 2, 3)), f(Set('a', 'b', 'c')))
//> pairApply2: [_](f: Set[_] => Option[_])(Option[Any], Option[Any])
def choose3[_](s:Set[_])=s.headOption //> choose3: [_](s: Set[_])Option[Any]
// Can't use choose2 else
// Description Resource Path Location Type
// type mismatch; found : Set[T] => Option[T] required: Set[_] => Option[_] scala.sc /Scala2 line 33 Scala Problem
// polymorphic expression cannot be instantiated to expected type; found : [T]Set[T] => Option[T] required: Set[_] => Option[_] scala.sc /Scala2 line 33 Scala Problem
pairApply2(choose3) //> res5: (Option[Any], Option[Any]) = (Some(1),Some(a))
// Note retuned as option[Any]
import shapeless._
import poly._
// size is a function from Ints or Strings or pairs to a 'size' defined
// by type specific cases
object size extends Poly1 {
implicit def caseInt = at[Int](x => 1)
implicit def caseString = at[String](_.length)
implicit def caseTuple[T, U]
(implicit st : Case.Aux[T, Int], su : Case.Aux[U, Int]) =
at[(T, U)](t => size(t._1)+size(t._2))
}
size(23) //> res0: Int = 1
size("foo") //> res1: Int = 3
size((23, "foo")) //> res2: Int = 4
size(((23, "foo"), 13)) //> res3: Int = 5
object incr extends Poly1 {
implicit def caseInt = at[Int](_+1)
implicit def caseString = at[String](s=>s+s.last)
implicit def caseTuple[T, U]
(implicit st : Case.Aux[T, T], su : Case.Aux[U, U]) =
at[(T, U)](t => (this(t._1),this(t._2)))
}
incr(23) //> res4: Int = 24
incr("hello") //> res5: String = helloo
incr((33,44)) //> res6: (Int, Int) = (34,45)
incr(("cat",44)) //> res7: (String, Int) = (catt,45)
import shapeless._ ; import syntax.singleton._ ; import record._
val book =
("author" ->> "Benjamin Pierce") ::
("title" ->> "Types and Programming Languages") ::
("id" ->> 262162091) ::
("price" ->> 44.11) ::
HNil
book("author") // Note result type ...
// res0: String = Benjamin Pierce
book("title") // Note result type ...
//res1: String = Types and Programming Languages
book("id") // Note result type ...
//res2: Int = 262162091
book("price") // Note result type ...
//res3: Double = 44.11
book.keys // Keys are materialized from singleton types encoded in value type
//res4: String("author") :: String("title") :: String("id") :: String("price") :: HNil =
// author :: title :: id :: price :: HNil
book.values
//res5: String :: String :: Int :: Double :: HNil =
// Benjamin Pierce :: Types and Programming Languages :: 262162091 :: 44.11 :: HNil
val newPrice = book("price")+2.0
//newPrice: Double = 46.11
val updated = book +("price" ->> newPrice) // Update an existing field
//updated: ... complex type elided ... =
// Benjamin Pierce :: Types and Programming Languages :: 262162091 :: 46.11 :: HNil
updated("price")
//res6: Double = 46.11
val extended = updated + ("inPrint" ->> true) // Add a new field
//extended: ... complex type elided ... =
// Benjamin Pierce :: Types and Programming Languages :: 262162091 :: 46.11 :: true :: HNil
val noId = extended - "id" // Removed a field
//noId: ... complex type elided ... =
// Benjamin Pierce :: Types and Programming Languages :: 46.11 :: true :: HNil
noId("id") // Attempting to access a missing field is a compile time error
// <console>:25: error: could not find implicit value for parameter selector ...
// noId("id")
def row(cols : Seq[String]) = cols.mkString("\"", "\",\"", "\"")
//> row: (cols: Seq[String])String
// Ensures number of items in hdrs and rows is the same. Magic!
def csv[N <: Nat]
(hdrs : Sized[Seq[String], N],
rows : List[Sized[Seq[String], N]]) = row(hdrs) :: rows.map(row(_))
//> csv: [N <: shapeless.Nat](hdrs: shapeless.Sized[Seq[String],N], rows: List[s
//| hapeless.Sized[Seq[String],N]])List[String]
val hdrs = Sized("Title", "Author") //> hdrs : shapeless.Sized[scala.collection.immutable.IndexedSeq[String],shapel
//| ess.nat._2] = shapeless.Sized@b45d9d5e
val rows = List(
Sized("Types and Programming Languages", "Benjamin Pierce"),
Sized("The Implementation of Functional Programming Languages", "Simon Peyton-Jones")
) //> rows : List[shapeless.Sized[scala.collection.immutable.IndexedSeq[String],s
//| hapeless.nat._2]] = List(shapeless.Sized@32bb6d1f, shapeless.Sized@15b5dd24)
//|
// hdrs and rows statically known to have the same number of columns
val formatted = csv(hdrs, rows) // Compiles
//> formatted : List[String] = List("Title","Author", "Types and Programming La
//| nguages","Benjamin Pierce", "The Implementation of Functional Programming La
//| nguages","Simon Peyton-Jones")
// extendedHdrs has the wrong number of columns for rows
val extendedHdrs = Sized("Title", "Author", "ISBN")
//> extendedHdrs : shapeless.Sized[scala.collection.immutable.IndexedSeq[String
//| ],shapeless.nat._3] = shapeless.Sized@7946158f
//val badFormatted = csv(extendedHdrs, rows) // Does not compile
import shapeless._
import poly._
import syntax.std.tuple._
// tuples can now be treated like HList
// head, tail, take, drop, split
(23, "foo", true).head //> res0: Int = 23
(23, "foo", true).tail //> res1: (String, Boolean) = (foo,true)
(23, "foo", true).drop(2) //> res2: (Boolean,) = (true,)
(23, "foo", true).take(2) //> res3: (Int, String) = (23,foo)
//splits at 1st element
(23, "foo", true).split(1) //> res4: ((Int,), (String, Boolean)) = ((23,),(foo,true))
//Wont' even compile as don't have that many elements in tuple. V type safe
//(23, "foo", true).split(4)
// prepend, append, concatenate
23 +: ("foo", true) //> res5: (Int, String, Boolean) = (23,foo,true)
(23, "foo") :+ true //> res6: (Int, String, Boolean) = (23,foo,true)
(23, "foo") ++ (true, 2.0) //> res7: (Int, String, Boolean, Double) = (23,foo,true,2.0)
// map, flatMap
object option extends (Id ~> Option) {
def apply[T](t: T) = Option(t)
}
(23, "foo", true) map option //> res8: (Option[Int], Option[String], Option[Boolean]) = (Some(23),Some(foo),S
//| ome(true))
((23, "foo"), (), (true, 2.0)) flatMap identity //> res9: (Int, String, Boolean, Double) = (23,foo,true,2.0)
object size extends Poly1 {
implicit def caseInt = at[Int](x => 1)
implicit def caseString = at[String](_.length)
implicit def caseTuple[T, U]
(implicit st : Case.Aux[T, Int], su : Case.Aux[U, Int]) =
at[(T, U)](t => size(t._1)+size(t._2))
}
object addSize extends Poly2 {
implicit def default[T](implicit st: size.Case.Aux[T, Int]) =
at[Int, T]{ (acc, t) => acc+size(t) }
}
// fold
(23, "foo", (13, "wibble")).foldLeft(0)(addSize) //> res10: Int = 11
// conversion to `HList`s and ordinary Scala `List`s
(23, "foo", true).productElements //> res11: shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.
//| HNil]]] = 23 :: foo :: true :: HNil
(23, "foo", true).toList //> res12: List[Any] = List(23, foo, true)
// zipper
import syntax.zipper._
(23, ("foo", true), 2.0).toZipper.right.down.put("bar").root.reify
//> res13: (Int, (String, Boolean), Double) = (23,(bar,true),2.0)
// index into tuple
val t = (23, "foo", true) //> t : (Int, String, Boolean) = (23,foo,true)
t(1) //> res1: String = foo
// Typesafe cast, return Option, not exception on fail
import syntax.typeable._
val l: Any = List(Vector("foo", "bar", "baz"), Vector("wibble"))
//> l : Any = List(Vector(foo, bar, baz), Vector(wibble))
l.cast[List[Vector[String]]] //> res0: Option[List[Vector[String]]] = Some(List(Vector(foo, bar, baz), Vector
//| (wibble)))
l.cast[List[Vector[Int]]] //> res1: Option[List[Vector[Int]]] = None
l.cast[List[List[String]]] //> res2: Option[List[List[String]]] = None
//TypeCases to retain type info
//Backticks to allow for odd value names
val `List[String]` = TypeCase[List[String]] //> List[String] : shapeless.TypeCase[List[String]] = shapeless.TypeCase$$anon$
//| 16@61dc03ce
val `List[Int]` = TypeCase[List[Int]] //> List[Int] : shapeless.TypeCase[List[Int]] = shapeless.TypeCase$$anon$16@50f
//| 8360d
val l2 = List(5, 10, 15) //> l2 : List[Int] = List(5, 10, 15)
(l2: Any) match {
case `List[String]`(List(s, _*)) => s.length
case `List[Int]`(List(i, _*)) => i+1
} //> res3: Int = 6
vs
// type erasure strikes again! Matches first case, even though List of Int
l2 match {
case s:List[String]=>s.length
case x:List[Int]=>x.head+1
} //> res4: Int = 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment