[关闭]
@MiloXia 2016-04-14T08:37:52.000000Z 字数 11271 阅读 1780

shapeless

scala


blog:
http://milessabin.com/blog/2011/12/19/shapeless-preview/
http://kanaka.io/blog/2015/11/09/shapeless-not-a-tutorial-part-1.html
stackoverflow:
https://stackoverflow.com/search?q=user:334519+[shapeless]

HList

import shapeless.{HList, HNil, ::}
how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Fhlist.scala

is covariant

  1. trait Fruit
  2. case class Apple() extends Fruit
  3. case class Pear() extends Fruit
  4. type FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNil
  5. type APAP = Apple :: Pear :: Apple :: Pear :: HNil
  6. val a: Apple = Apple()
  7. val p: Pear = Pear()
  8. val apap: APAP = a :: p :: a :: p :: HNil
  9. val ffff: FFFF = apap // APAP <: FFFF
  10. // discard precise typing
  11. val ffff: FFFF = apap.unify
  12. val apap2: Option[APAP] = ffff.cast[APAP]
  13. apap2.get == apap

基本list操作

  1. val mySecondHList : String :: Int :: java.util.UUID :: HNil = "foo" :: 42 :: java.util.UUID.randomUUID() :: HNil
  2. mySecondHList.head // foo
  3. mySecondHList.tail //
  4. 42 :: 49fa9131-eb09-4a14-b86e-e358b25bfbc7 :: HNil
  5. mySecondHList.tail.tail.tail.head //HNil.head 会报错-编译不过
  6. val l1 = 2 :: "a" :: HNil
  7. l1.toList // List(2, a)
  8. l1.reverse
  9. //Prepend :::
  10. l1 ::: l2

At & Drop & Split

  1. import nat._
  2. println(l1(_0), l1(_1)) // (2,a)
  3. println(l1.drop(_0)) //2 :: a :: true :: HNil
  4. println(l1.drop(_1)) //a :: true :: HNil
  5. println(l1.take(_1)) //2 :: HNil
  6. println(l1.split(_0)) //(HNil,2 :: a :: true :: HNil)
  7. println(l1.split(_1)) //(2 :: HNil,a :: true :: HNil)

还有filter,update, zip,diff 等各种操作

zipper for traversal and persistent update

how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Fzipper.scala

  1. import syntax.zipper._
  2. val l1 = 2 :: "a" :: true :: HNil
  3. //Traversal
  4. val z1 = l1.toZipper
  5. println(z1) //Zipper(HNil,2 :: a :: true :: HNil,None)
  6. println(z1.get) //2
  7. println(z1.right.get) //a
  8. println(z1.right.right.get) // true
  9. val z2 = l1.toZipper.last
  10. println(z2) // Zipper(true :: a :: 2 :: HNil,HNil,None)
  11. println(z2.left.get) //true
  12. println(z2.first.get) //2
  13. //Update
  14. println(z1.right.put("wibble").reify) // 2 :: wibble :: true :: HNil
  15. println(z1.right.delete.reify) // 2 :: true :: HNil
  16. println(z1.insert("bar").reify) // bar :: 2 :: a :: true :: HNil
  17. println(z1.right.modify(_.toUpperCase).reify) // 2 :: A :: true :: HNil
  18. //TypeIndexing 等于 hlist的selectManyType
  19. println(z1.rightTo[Boolean].get) // true
  20. //NatIndexing 等于 hlist的select
  21. println(z1.rightBy(1).get) // a

pattern match

  1. val hlist1 = 1 :: "foo" :: true :: HNil
  2. val hlist2 = "foo" :: 2 :: true :: HNil
  3. def thirdIsTrue(hlist: HList) = hlist match {
  4. case (_:Int) :: (_:String) :: true :: HNil => true
  5. case _ => false
  6. }
  7. thirdIsTrue(hlist1) //true

tuple to HList and back

  1. val t1 = (23, "foo", 2.0, true)
  2. val l1 = t1.hlisted
  3. h1 == 23 :: "foo" :: 2.0 :: true :: HNil
  4. val t2 = l1.tupled
  5. t1 == t2

map - polymorphic function

  1. //polymorphic function -- 也是个自然变换
  2. object choose extends (Set ~> Option) {
  3. def apply[T](s: Set[T]) = s.headOption
  4. }
  5. //KList’s (HList’s whose elements share a common outer type constructor)
  6. type SISS = Set[Int] :: Set[String] :: HNil
  7. type OIOS = Option[Int] :: Option[String] :: HNil
  8. val sets: SISS = Set(1) :: Set("foo") :: HNil
  9. val opts: OIOS = sets map choose
  10. println(opts, opts == Option(1) :: Option("foo") :: HNil)
  11. //(Some(1) :: Some(foo) :: HNil,true)
  12. val l1 = 2 :: "a" :: HNil
  13. object test extends (Id ~> Const[Int]#λ) { //或写成 (Id ~>> Int)
  14. def apply[T](s: T): Int = 0
  15. }
  16. println(l1 map test) // 0 :: 0 :: HNil

flatMap

  1. import poly._
  2. val l1 = 2 :: "a" :: true :: HNil
  3. object incInt extends (Int >-> Int)(_ + 1)
  4. val l11 = l1 flatMap incInt
  5. println(l11) // 3 :: HNil

align 更换type的顺序

  1. type M0 = Int :: String :: Boolean :: HNil
  2. val m0 = 13 :: "bar" :: false :: HNil
  3. val l = 23 :: "foo" :: true :: HNil
  4. val a0 = l.align(m0)
  5. assertTypedEquals[M0](23 :: "foo" :: true :: HNil, a0)

foldLeft & collect

  1. object combine extends Poly {
  2. implicit def caseCharString = use((c : Char, s : String) => s.indexOf(c))
  3. implicit def caseIntBoolean = use((i : Int, b : Boolean) => if ((i >= 0) == b) "pass" else "fail")
  4. }
  5. val c1a = combine('o', "foo") // 1
  6. val c1b = combine(c1a, true) //pass
  7. println(c1a, c1b)
  8. val lc = "foo" :: true :: HNil
  9. val f1 = lc.foldLeft('o')(combine)
  10. println(f1)
  11. //验证类型上是否正确
  12. val tf3 = implicitly[LeftFolder[String :: Boolean :: HNil, Char, combine.type]]

Typeable

how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Ftypeable.scala

  1. import syntax.typeable._
  2. import java.{ lang => jl }
  3. val l: Any = 23L
  4. val cl = l.cast[Long]
  5. //BoxedPrimitives
  6. val i: Any = 23
  7. val ci = i.cast[jl.Integer]
  8. //Unerased
  9. val li: Any = List(1, 2, 3, 4)
  10. val cli = li.cast[List[Int]]
  11. val lvs: Any = List(Vector("foo", "bar", "baz"), Vector("wibble"))
  12. val clvs = lvs.cast[List[Vector[String]]]
  13. //HList
  14. val lisdb: Any = 23 :: "foo" :: false :: HNil
  15. val clisdb = lisdb.cast[Int :: String :: Boolean :: HNil]
  16. assertTrue(clisdb.isDefined)
  17. //Coproduct
  18. type CP = Int :+: String :+: Double :+: Boolean :+: CNil
  19. val cpi: Any = Coproduct[CP](23)
  20. val ccpi = cpi.cast[CP]
  21. assertTrue(ccpi.isDefined)

Poly

实现原理:http://milessabin.com/blog/2012/05/10/shapeless-polymorphic-function-values-2/
没有part3, part3就是现在的Case模式(type specific cases)吧
type specific cases 原理: http://kanaka.io/blog/2015/11/09/shapeless-not-a-tutorial-part-1.html
atT 返回一个shapeless.poly.Case[F,T::HNil]

We can now see how the apply method works for Poly1 :

For any Poly1 subtype F, given a argument of type T, an instance ofshapeless.poly.Case[F,T::HNil] is searched in the implicit scope.
If we have defined a parameterless implicit method in object F that returnsatT (f being a function defined on T), this method will be selected since it returns an instance of shapeless.poly.Case[F, T::HNil] and
The result of applying F to an argument of type T will be the one of applyingf to that argument.

  1. //如上的combine,choose,test, incInt
  2. object zero extends Poly0 {
  3. implicit val zeroInt = at(0)
  4. implicit val zeroDouble = at(0.0)
  5. implicit val zeroString = at("")
  6. implicit def zeroList[T] = at[List[T]](Nil)
  7. }
  8. object size extends Poly1 {
  9. implicit def default[T] = at[T](_ => 1)
  10. implicit def caseInt = at[Int](_ => 1)
  11. implicit def caseString = at[String](_.length)
  12. implicit def caseList[T] = at[List[T]](_.length)
  13. implicit def caseOption[T](implicit st : Case.Aux[T, Int]) = at[Option[T]](t => 1+(t map size).getOrElse(0))
  14. //Case.Aux[T, Int] is Case[I]{ type Result = O }
  15. implicit def caseTuple[T, U](implicit st : Case.Aux[T, Int], su : Case.Aux[U, Int]) = at[(T, U)]{ case (t, u) => size(t)+size(u) }
  16. }
  17. //可测 implicitly[size.Case[Int]]
  18. //~> 继承自Poly1,表示自然变换,F[_] -> G[_]
  19. //~>> 是 type ~>>[F[_], R] = ~>[F, Const[R]#λ] 不需要打λ符号
  20. object plus extends Poly2 {
  21. implicit val caseInt = at[Int, Int](_ + _)
  22. implicit val caseDouble = at[Double, Double](_ + _)
  23. implicit val caseString = at[String, String](_ + _)
  24. implicit def caseList[T] = at[List[T], List[T]](_ ::: _)
  25. }
  26. plus(1, 2)
  27. object isd extends Poly3 {
  28. implicit val default = at[Int, String, Double] { //default只是个名字,没啥特别的
  29. case (i, s, d) => s"i: $i, s: $s, d: $d"
  30. }
  31. }
  32. isd(1, "foo", 2.0)

Generic

it provides the hlist representation of a case class (or a tuple) and operations to convert an instance of the case class to its hlist representation, and back.

  1. case class User(id: Long, name: String)
  2. val genUser = Generic[User]
  3. println(genUser.to(User(1L, "Homer"))) //1 :: Homer :: HNil
  4. println(genUser.from(2L :: "Marge" :: HNil)) //User(2,Marge)

Records and LabelledGeneric

Singleton types

42L的类型是Long(42),编译期私有

Record

record = fields的hlist
field = label (singleton type) + type (value type) = FieldType[K, +V] = V with KeyTag[K, V] (K is label)
how to use : https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Frecords.scala

  1. val field = 1 ->> Some("value")
  2. //Some[String] with shapeless.labelled.KeyTag[Int(1),Some[String]] = Some(value)
  3. case class Book(author: String, title: String, id: Int, price: Double)
  4. val tapl = Book("Benjamin Pierce", "Types and Programming Languages", 262162091, 44.11)
  5. val taplRecord =
  6. ("author" ->> "Benjamin Pierce") ::
  7. ("title" ->> "Types and Programming Languages") ::
  8. ("id" ->> 262162091) ::
  9. ("price" ->> 44.11) ::
  10. HNil
  11. val v1 = taplRecord.get("author")

LabelledGeneric

represents case classes as records, using singleton-typed Symbols as keys (field names)

  1. case class Rectangle(width: Int, height: Int)
  2. val genRectangle = LabelledGeneric[Rectangle]
  3. val repr = genRectangle.to(Rectangle(1,3))
  4. println(repr) //1 :: 3 :: HNil
  5. println(repr.get('width)) //1
  6. val modified = repr.updated('height, 42)
  7. println(modified) //1 :: 42 :: HNil
  8. println(genRectangle.from(modified)) //Rectangle(1,42)

Lenses

how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Flenses.scala

  1. import lens._
  2. case class Address(street : String, city : String, postcode : String)
  3. case class Person(name : String, age : Int, address : Address)
  4. val address = Address("Southover Street", "Brighton", "BN2 9UA")
  5. val person = Person("Joe Grey", 37, address)
  6. //create lenses
  7. val nameLens: Lens[Person, String]
  8. val ageLens: Lens[Person, Int]
  9. val addressLens: Lens[Person, Address]
  10. val streetLens: Lens[Person, String]
  11. val cityLens: Lens[Person, String]
  12. val postcodeLens: Lens[Person, String]
  13. val age1 = ageLens.get(person) //37
  14. val person2 = ageLens.set(person)(38)
  15. val street1 = streetLens.get(person) //"Southover Street"
  16. //Compose
  17. val addressLens_2 = lens[Person] >> 2
  18. val streetLens_2 = lens[Address] >> 0
  19. val personStreetLens1 = streetLens compose addressLens
  20. val street1 = personStreetLens1.get(person) //"Southover Street"
  21. //Tuples
  22. type ISDB = (Int, (String, (Double, Boolean)))
  23. val tp = (23, ("foo", (2.0, false)))
  24. val lens0 = lens[ISDB] >> 0
  25. val lens1 = lens[ISDB] >> 1
  26. val lens10 = lens[ISDB] >> 1 >> 0
  27. val lens11 = lens[ISDB] >> 1 >> 1
  28. val lens110 = lens[ISDB] >> 1 >> 1 >> 0
  29. val lens111 = lens[ISDB] >> 1 >> 1 >> 1
  30. val i = lens0.get(tp)
  31. //HLists
  32. type ISB = Int :: String :: Boolean :: HNil
  33. val l = 23 :: "foo" :: true :: HNil
  34. val lens0 = hlistNthLens[ISB, _0]
  35. val lensI = hlistSelectLens[ISB, Int]
  36. val lens1 = hlistNthLens[ISB, _1]
  37. val lensS = hlistSelectLens[ISB, String]
  38. val lens2 = hlistNthLens[ISB, _2]
  39. val lensB = hlistSelectLens[ISB, Boolean]
  40. val i = lens0.get(l)
  41. //Records & Sets & Maps

Coproduct

how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Fcoproduct.scala

  1. type ISB = Int :+: String :+: Boolean :+: CNil
  2. val foo1 = Coproduct[ISB](23)
  3. val foo2 = Coproduct[ISB]("foo")
  4. val foo3 = Coproduct[ISB](true)
  5. val sel1a = foo1.select[Int] // Some(23)
  6. val sel1b = foo1.select[String] // None
  7. def typed[A](a: A) = true
  8. def cpMatch(v: ISB) = v match {
  9. case Inl(x) =>
  10. typed[Int](x); x
  11. case Inr(Inl(x)) =>
  12. typed[String](x); x
  13. case Inr(Inr(Inl(x))) =>
  14. typed[Boolean](x); x
  15. case Inr(Inr(Inr(_))) => ??? // This impossible case required for exhaustivity
  16. }
  17. println(cpMatch(foo1)) //23
  18. //flatMap & map
  19. val foo1b = foo1 map size
  20. println(foo1b) // 1
  21. object coIdentity extends Poly1 {
  22. implicit def default[A] = at[A](a => Coproduct[A :+: CNil](a))
  23. }
  24. val foo1c = foo1.flatMap(coIdentity)

examples:

Converting Map[String,Any] to a case class using Shapeless

  1. //Here's an off-the-cuff, mostly untested solution. First for the type class:
  2. import shapeless._, labelled.{ FieldType, field }
  3. trait FromMap[L <: HList] {
  4. def apply(m: Map[String, Any]): Option[L]
  5. }
  6. //And then the instances:
  7. trait LowPriorityFromMap {
  8. implicit def hconsFromMap1[K <: Symbol, V, T <: HList](implicit
  9. witness: Witness.Aux[K],
  10. typeable: Typeable[V],
  11. fromMapT: FromMap[T]
  12. ): FromMap[FieldType[K, V] :: T] = new FromMap[FieldType[K, V] :: T] {
  13. def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
  14. v <- m.get(witness.value.name)
  15. h <- typeable.cast(v)
  16. t <- fromMapT(m)
  17. } yield field[K](h) :: t
  18. }
  19. }
  20. object FromMap extends LowPriorityFromMap {
  21. implicit val hnilFromMap: FromMap[HNil] = new FromMap[HNil] {
  22. def apply(m: Map[String, Any]): Option[HNil] = Some(HNil)
  23. }
  24. implicit def hconsFromMap0[K <: Symbol, V, R <: HList, T <: HList](implicit
  25. witness: Witness.Aux[K],
  26. gen: LabelledGeneric.Aux[V, R],
  27. fromMapH: FromMap[R],
  28. fromMapT: FromMap[T]
  29. ): FromMap[FieldType[K, V] :: T] = new FromMap[FieldType[K, V] :: T] {
  30. def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
  31. v <- m.get(witness.value.name)
  32. r <- Typeable[Map[String, Any]].cast(v)
  33. h <- fromMapH(r)
  34. t <- fromMapT(m)
  35. } yield field[K](gen.from(h)) :: t
  36. }
  37. }
  38. //And then a helper class for convenience:
  39. class ConvertHelper[A] {
  40. def from[R <: HList](m: Map[String, Any])(implicit
  41. gen: LabelledGeneric.Aux[A, R],
  42. fromMap: FromMap[R]
  43. ): Option[A] = fromMap(m).map(gen.from(_))
  44. }
  45. def to[A]: ConvertHelper[A] = new ConvertHelper[A]
  46. And the example:
  47. case class Address(street: String, zip: Int)
  48. case class Person(name: String, address: Address)
  49. val mp = Map(
  50. "name" -> "Tom",
  51. "address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
  52. )
  53. //And finally:
  54. scala> to[Person].from(mp)
  55. res0: Option[Person] = Some(Person(Tom,Address(Jefferson st,10000)))
  56. //This will only work for case classes whose members are either Typeable or other case classes whose members are either Typeable or other case classes… (and so on).
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注