@MiloXia
2016-04-14T08:37:52.000000Z
字数 11271
阅读 2141
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]
import shapeless.{HList, HNil, ::}
how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Fhlist.scala
trait Fruitcase class Apple() extends Fruitcase class Pear() extends Fruittype FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNiltype APAP = Apple :: Pear :: Apple :: Pear :: HNilval a: Apple = Apple()val p: Pear = Pear()val apap: APAP = a :: p :: a :: p :: HNilval ffff: FFFF = apap // APAP <: FFFF// discard precise typingval ffff: FFFF = apap.unifyval apap2: Option[APAP] = ffff.cast[APAP]apap2.get == apap
val mySecondHList : String :: Int :: java.util.UUID :: HNil = "foo" :: 42 :: java.util.UUID.randomUUID() :: HNilmySecondHList.head // foomySecondHList.tail //42 :: 49fa9131-eb09-4a14-b86e-e358b25bfbc7 :: HNilmySecondHList.tail.tail.tail.head //HNil.head 会报错-编译不过val l1 = 2 :: "a" :: HNill1.toList // List(2, a)l1.reverse//Prepend :::l1 ::: l2
import nat._println(l1(_0), l1(_1)) // (2,a)println(l1.drop(_0)) //2 :: a :: true :: HNilprintln(l1.drop(_1)) //a :: true :: HNilprintln(l1.take(_1)) //2 :: HNilprintln(l1.split(_0)) //(HNil,2 :: a :: true :: HNil)println(l1.split(_1)) //(2 :: HNil,a :: true :: HNil)
还有filter,update, zip,diff 等各种操作
how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Fzipper.scala
import syntax.zipper._val l1 = 2 :: "a" :: true :: HNil//Traversalval z1 = l1.toZipperprintln(z1) //Zipper(HNil,2 :: a :: true :: HNil,None)println(z1.get) //2println(z1.right.get) //aprintln(z1.right.right.get) // trueval z2 = l1.toZipper.lastprintln(z2) // Zipper(true :: a :: 2 :: HNil,HNil,None)println(z2.left.get) //trueprintln(z2.first.get) //2//Updateprintln(z1.right.put("wibble").reify) // 2 :: wibble :: true :: HNilprintln(z1.right.delete.reify) // 2 :: true :: HNilprintln(z1.insert("bar").reify) // bar :: 2 :: a :: true :: HNilprintln(z1.right.modify(_.toUpperCase).reify) // 2 :: A :: true :: HNil//TypeIndexing 等于 hlist的selectManyTypeprintln(z1.rightTo[Boolean].get) // true//NatIndexing 等于 hlist的selectprintln(z1.rightBy(1).get) // a
val hlist1 = 1 :: "foo" :: true :: HNilval hlist2 = "foo" :: 2 :: true :: HNildef thirdIsTrue(hlist: HList) = hlist match {case (_:Int) :: (_:String) :: true :: HNil => truecase _ => false}thirdIsTrue(hlist1) //true
val t1 = (23, "foo", 2.0, true)val l1 = t1.hlistedh1 == 23 :: "foo" :: 2.0 :: true :: HNilval t2 = l1.tupledt1 == t2
//polymorphic function -- 也是个自然变换object choose extends (Set ~> Option) {def apply[T](s: Set[T]) = s.headOption}//KList’s (HList’s whose elements share a common outer type constructor)type SISS = Set[Int] :: Set[String] :: HNiltype OIOS = Option[Int] :: Option[String] :: HNilval sets: SISS = Set(1) :: Set("foo") :: HNilval opts: OIOS = sets map chooseprintln(opts, opts == Option(1) :: Option("foo") :: HNil)//(Some(1) :: Some(foo) :: HNil,true)val l1 = 2 :: "a" :: HNilobject test extends (Id ~> Const[Int]#λ) { //或写成 (Id ~>> Int)def apply[T](s: T): Int = 0}println(l1 map test) // 0 :: 0 :: HNil
import poly._val l1 = 2 :: "a" :: true :: HNilobject incInt extends (Int >-> Int)(_ + 1)val l11 = l1 flatMap incIntprintln(l11) // 3 :: HNil
type M0 = Int :: String :: Boolean :: HNilval m0 = 13 :: "bar" :: false :: HNilval l = 23 :: "foo" :: true :: HNilval a0 = l.align(m0)assertTypedEquals[M0](23 :: "foo" :: true :: HNil, a0)
object combine extends Poly {implicit def caseCharString = use((c : Char, s : String) => s.indexOf(c))implicit def caseIntBoolean = use((i : Int, b : Boolean) => if ((i >= 0) == b) "pass" else "fail")}val c1a = combine('o', "foo") // 1val c1b = combine(c1a, true) //passprintln(c1a, c1b)val lc = "foo" :: true :: HNilval f1 = lc.foldLeft('o')(combine)println(f1)//验证类型上是否正确val tf3 = implicitly[LeftFolder[String :: Boolean :: HNil, Char, combine.type]]
how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Ftypeable.scala
import syntax.typeable._import java.{ lang => jl }val l: Any = 23Lval cl = l.cast[Long]//BoxedPrimitivesval i: Any = 23val ci = i.cast[jl.Integer]//Unerasedval li: Any = List(1, 2, 3, 4)val cli = li.cast[List[Int]]val lvs: Any = List(Vector("foo", "bar", "baz"), Vector("wibble"))val clvs = lvs.cast[List[Vector[String]]]//HListval lisdb: Any = 23 :: "foo" :: false :: HNilval clisdb = lisdb.cast[Int :: String :: Boolean :: HNil]assertTrue(clisdb.isDefined)//Coproducttype CP = Int :+: String :+: Double :+: Boolean :+: CNilval cpi: Any = Coproduct[CP](23)val ccpi = cpi.cast[CP]assertTrue(ccpi.isDefined)
实现原理: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.
//如上的combine,choose,test, incIntobject zero extends Poly0 {implicit val zeroInt = at(0)implicit val zeroDouble = at(0.0)implicit val zeroString = at("")implicit def zeroList[T] = at[List[T]](Nil)}object size extends Poly1 {implicit def default[T] = at[T](_ => 1)implicit def caseInt = at[Int](_ => 1)implicit def caseString = at[String](_.length)implicit def caseList[T] = at[List[T]](_.length)implicit def caseOption[T](implicit st : Case.Aux[T, Int]) = at[Option[T]](t => 1+(t map size).getOrElse(0))//Case.Aux[T, Int] is Case[I]{ type Result = O }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) }}//可测 implicitly[size.Case[Int]]//~> 继承自Poly1,表示自然变换,F[_] -> G[_]//~>> 是 type ~>>[F[_], R] = ~>[F, Const[R]#λ] 不需要打λ符号object plus extends Poly2 {implicit val caseInt = at[Int, Int](_ + _)implicit val caseDouble = at[Double, Double](_ + _)implicit val caseString = at[String, String](_ + _)implicit def caseList[T] = at[List[T], List[T]](_ ::: _)}plus(1, 2)object isd extends Poly3 {implicit val default = at[Int, String, Double] { //default只是个名字,没啥特别的case (i, s, d) => s"i: $i, s: $s, d: $d"}}isd(1, "foo", 2.0)
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.
case class User(id: Long, name: String)val genUser = Generic[User]println(genUser.to(User(1L, "Homer"))) //1 :: Homer :: HNilprintln(genUser.from(2L :: "Marge" :: HNil)) //User(2,Marge)
42L的类型是Long(42),编译期私有
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
val field = 1 ->> Some("value")//Some[String] with shapeless.labelled.KeyTag[Int(1),Some[String]] = Some(value)case class Book(author: String, title: String, id: Int, price: Double)val tapl = Book("Benjamin Pierce", "Types and Programming Languages", 262162091, 44.11)val taplRecord =("author" ->> "Benjamin Pierce") ::("title" ->> "Types and Programming Languages") ::("id" ->> 262162091) ::("price" ->> 44.11) ::HNilval v1 = taplRecord.get("author")
represents case classes as records, using singleton-typed Symbols as keys (field names)
case class Rectangle(width: Int, height: Int)val genRectangle = LabelledGeneric[Rectangle]val repr = genRectangle.to(Rectangle(1,3))println(repr) //1 :: 3 :: HNilprintln(repr.get('width)) //1val modified = repr.updated('height, 42)println(modified) //1 :: 42 :: HNilprintln(genRectangle.from(modified)) //Rectangle(1,42)
how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Flenses.scala
import lens._case class Address(street : String, city : String, postcode : String)case class Person(name : String, age : Int, address : Address)val address = Address("Southover Street", "Brighton", "BN2 9UA")val person = Person("Joe Grey", 37, address)//create lensesval nameLens: Lens[Person, String]val ageLens: Lens[Person, Int]val addressLens: Lens[Person, Address]val streetLens: Lens[Person, String]val cityLens: Lens[Person, String]val postcodeLens: Lens[Person, String]val age1 = ageLens.get(person) //37val person2 = ageLens.set(person)(38)val street1 = streetLens.get(person) //"Southover Street"//Composeval addressLens_2 = lens[Person] >> 2val streetLens_2 = lens[Address] >> 0val personStreetLens1 = streetLens compose addressLensval street1 = personStreetLens1.get(person) //"Southover Street"//Tuplestype ISDB = (Int, (String, (Double, Boolean)))val tp = (23, ("foo", (2.0, false)))val lens0 = lens[ISDB] >> 0val lens1 = lens[ISDB] >> 1val lens10 = lens[ISDB] >> 1 >> 0val lens11 = lens[ISDB] >> 1 >> 1val lens110 = lens[ISDB] >> 1 >> 1 >> 0val lens111 = lens[ISDB] >> 1 >> 1 >> 1val i = lens0.get(tp)//HListstype ISB = Int :: String :: Boolean :: HNilval l = 23 :: "foo" :: true :: HNilval lens0 = hlistNthLens[ISB, _0]val lensI = hlistSelectLens[ISB, Int]val lens1 = hlistNthLens[ISB, _1]val lensS = hlistSelectLens[ISB, String]val lens2 = hlistNthLens[ISB, _2]val lensB = hlistSelectLens[ISB, Boolean]val i = lens0.get(l)//Records & Sets & Maps
how to use: https://github.com/milessabin/shapeless/blob/master/core%2Fsrc%2Ftest%2Fscala%2Fshapeless%2Fcoproduct.scala
type ISB = Int :+: String :+: Boolean :+: CNilval foo1 = Coproduct[ISB](23)val foo2 = Coproduct[ISB]("foo")val foo3 = Coproduct[ISB](true)val sel1a = foo1.select[Int] // Some(23)val sel1b = foo1.select[String] // Nonedef typed[A](a: A) = truedef cpMatch(v: ISB) = v match {case Inl(x) =>typed[Int](x); xcase Inr(Inl(x)) =>typed[String](x); xcase Inr(Inr(Inl(x))) =>typed[Boolean](x); xcase Inr(Inr(Inr(_))) => ??? // This impossible case required for exhaustivity}println(cpMatch(foo1)) //23//flatMap & mapval foo1b = foo1 map sizeprintln(foo1b) // 1object coIdentity extends Poly1 {implicit def default[A] = at[A](a => Coproduct[A :+: CNil](a))}val foo1c = foo1.flatMap(coIdentity)
//Here's an off-the-cuff, mostly untested solution. First for the type class:import shapeless._, labelled.{ FieldType, field }trait FromMap[L <: HList] {def apply(m: Map[String, Any]): Option[L]}//And then the instances:trait LowPriorityFromMap {implicit def hconsFromMap1[K <: Symbol, V, T <: HList](implicitwitness: Witness.Aux[K],typeable: Typeable[V],fromMapT: FromMap[T]): FromMap[FieldType[K, V] :: T] = new FromMap[FieldType[K, V] :: T] {def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {v <- m.get(witness.value.name)h <- typeable.cast(v)t <- fromMapT(m)} yield field[K](h) :: t}}object FromMap extends LowPriorityFromMap {implicit val hnilFromMap: FromMap[HNil] = new FromMap[HNil] {def apply(m: Map[String, Any]): Option[HNil] = Some(HNil)}implicit def hconsFromMap0[K <: Symbol, V, R <: HList, T <: HList](implicitwitness: Witness.Aux[K],gen: LabelledGeneric.Aux[V, R],fromMapH: FromMap[R],fromMapT: FromMap[T]): FromMap[FieldType[K, V] :: T] = new FromMap[FieldType[K, V] :: T] {def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {v <- m.get(witness.value.name)r <- Typeable[Map[String, Any]].cast(v)h <- fromMapH(r)t <- fromMapT(m)} yield field[K](gen.from(h)) :: t}}//And then a helper class for convenience:class ConvertHelper[A] {def from[R <: HList](m: Map[String, Any])(implicitgen: LabelledGeneric.Aux[A, R],fromMap: FromMap[R]): Option[A] = fromMap(m).map(gen.from(_))}def to[A]: ConvertHelper[A] = new ConvertHelper[A]And the example:case class Address(street: String, zip: Int)case class Person(name: String, address: Address)val mp = Map("name" -> "Tom","address" -> Map("street" -> "Jefferson st", "zip" -> 10000))//And finally:scala> to[Person].from(mp)res0: Option[Person] = Some(Person(Tom,Address(Jefferson st,10000)))//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).