Skip to content
This repository
Browse code

Major rework of polymorphic values.

  • Loading branch information...
commit 447aae489dd1b7b47fc2d7cc027ca73844a76550 1 parent ecc9eb6
Miles Sabin authored
9 examples/src/main/scala/shapeless/examples/flatten.scala
@@ -70,9 +70,10 @@ object FlattenExample {
70 70 val l1 = f1.toList // Inferred type is List[Int]
71 71 typed[List[Int]](l1)
72 72
73   - object toDouble extends (Id ~> Const[Double]#λ) with NoDefault
74   - implicit def intToDouble = toDouble.λ[Int](_.toDouble)
75   - implicit def doubleToDouble = toDouble.λ[Double](t => t)
  73 + object toDouble extends Poly {
  74 + implicit def caseInt = case1[Int](_.toDouble)
  75 + implicit def caseDouble = case1[Double](identity)
  76 + }
76 77
77 78 val t2 = (1, ((2, 3.0), 4))
78 79 val f2 = flatten(t2) // Inferred type is Int :: Int :: Double :: Int :: HNil
@@ -84,4 +85,4 @@ object FlattenExample {
84 85 val f3 = flatten(t3) // Inferred type is Int :: Boolean :: Double :: String :: String :: Int :: Boolean :: HNil
85 86 val t3b = f3.tupled // Inferred type is (Int, Boolean, Double, String, String, Int, Boolean)
86 87 typed[(Int, Boolean, Double, String, String, Int, Boolean)](t3b)
87   -}
  88 +}
7 examples/src/main/scala/shapeless/examples/stackoverflow.scala
@@ -16,7 +16,7 @@
16 16
17 17 package shapeless.examples
18 18
19   -class StackOverflow2 {
  19 +object StackOverflow2 {
20 20 // http://stackoverflow.com/questions/8270526
21 21
22 22 import shapeless._
@@ -40,13 +40,12 @@ class StackOverflow2 {
40 40 val a2 = ApplyA(A2.apply _, a :: a :: HNil)
41 41 }
42 42
43   -class StackOverflow3 {
  43 +object StackOverflow3 {
44 44 // http://stackoverflow.com/questions/8681491
45 45
46 46 import shapeless._
47 47 import HList._
48 48 import Functions._
49   - import Poly._
50 49 import TypeOperators._
51 50
52 51 case class Input[T](value: T)
@@ -61,7 +60,7 @@ class StackOverflow3 {
61 60 }
62 61
63 62 case class Foo(input1 : Int, input2 : String)
64   -
  63 +
65 64 object FooBuilder extends Preprocessor((Foo.apply _).hlisted)
66 65
67 66 val foo = FooBuilder(Input(23) :: Input("foo") :: HNil)
22 src/main/scala/shapeless/conversions.scala
@@ -37,30 +37,16 @@ object Tuples {
37 37 /**
38 38 * Higher ranked function which converts `Tuples` to `HLists`.
39 39 */
40   - object hlisted {
41   - def apply[T <: Product](t : T)(implicit hlister : HLister[T]) : hlister.Out = hlister(t)
  40 + object hlisted extends Poly {
  41 + implicit def caseProduct[T <: Product](implicit hlister : HLister[T]) = case1[T](hlister(_))
42 42 }
43   - implicit def hlisted1[T <: Product](implicit hlister : HLister[T]) =
44   - new Case[hlisted.type, T => hlister.Out](hlister.apply(_))
45 43
46 44 /**
47   - * Monomorphic instantiator for [[shapeless.Tuples.hlisted]].
48   - */
49   - implicit def univInstHListed[F, G](h : hlisted.type)(implicit c : Case[hlisted.type, F => G]) : F => G = c.value
50   -
51   - /**
52 45 * Higher ranked function which converts `HLists` to `Tuples`.
53 46 */
54   - object tupled {
55   - def apply[L <: HList](l : L)(implicit tupler : Tupler[L]) : tupler.Out = tupler(l)
  47 + object tupled extends Poly {
  48 + implicit def caseHList[L <: HList](implicit tupler : Tupler[L]) = case1[L](tupler(_))
56 49 }
57   - implicit def tupled1[L <: HList](implicit tupler : Tupler[L]) =
58   - new Case[tupled.type, L => tupler.Out](tupler.apply(_))
59   -
60   - /**
61   - * Monomorphic instantiator for [[shapeless.Tuples.tupled]].
62   - */
63   - implicit def univInstTupled[F, G](t : tupled.type)(implicit c : Case[tupled.type, F => G]) : F => G = c.value
64 50 }
65 51
66 52 /**
14 src/main/scala/shapeless/hlist.scala
@@ -393,13 +393,15 @@ object Mapper {
393 393 }
394 394
395 395 object MapperAux {
  396 + import Poly._
  397 +
396 398 implicit def hnilMapper1[HF] = new MapperAux[HF, HNil, HNil] {
397 399 def apply(l : HNil) = HNil
398 400 }
399 401
400   - implicit def hlistMapper1[HF, InH, OutH, InT <: HList, OutT <: HList]
401   - (implicit hc : Case[HF, InH => OutH], mt : MapperAux[HF, InT, OutT]) = new MapperAux[HF, InH :: InT, OutH :: OutT] {
402   - def apply(l : InH :: InT) = hc(l.head) :: mt(l.tail)
  402 + implicit def hlistMapper1[HF <: Poly, InH, OutH, InT <: HList, OutT <: HList]
  403 + (implicit hc : Pullback1Aux[HF, InH, OutH], mt : MapperAux[HF, InT, OutT]) = new MapperAux[HF, InH :: InT, OutH :: OutT] {
  404 + def apply(l : InH :: InT) = hc.value(l.head) :: mt(l.tail)
403 405 }
404 406 }
405 407
@@ -447,13 +449,15 @@ trait LeftFolder[L <: HList, R, HF] {
447 449 }
448 450
449 451 object LeftFolder {
  452 + import Poly._
  453 +
450 454 implicit def hnilLeftFolder[R, HF] = new LeftFolder[HNil, R, HF] {
451 455 def apply(l : HNil, in : R, op : (R, R) => R) = in
452 456 }
453 457
454   - implicit def hlistLeftFolder[H, T <: HList, R, HF](implicit hc : Case[HF, H => R], tf : LeftFolder[T, R, HF]) =
  458 + implicit def hlistLeftFolder[H, T <: HList, R, HF <: Poly](implicit hc : Pullback1Aux[HF, H, R], tf : LeftFolder[T, R, HF]) =
455 459 new LeftFolder[H :: T, R, HF] {
456   - def apply(l : H :: T, in : R, op : (R, R) => R) = tf(l.tail, op(in, hc(l.head)), op)
  460 + def apply(l : H :: T, in : R, op : (R, R) => R) = tf(l.tail, op(in, hc.value(l.head)), op)
457 461 }
458 462 }
459 463
1  src/main/scala/shapeless/lift.scala
@@ -17,7 +17,6 @@
17 17 package shapeless
18 18
19 19 object Lift {
20   - import Poly._
21 20 import Functions._
22 21
23 22 /**
234 src/main/scala/shapeless/poly.scala
@@ -16,155 +16,171 @@
16 16
17 17 package shapeless
18 18
  19 +import TypeOperators._
  20 +
19 21 /**
20 22 * Type-specific case of a polymorphic value.
21 23 *
22 24 * @author Miles Sabin
23 25 */
24   -case class Case[-P, T](t : T) {
25   - /** The monomorphic value at `T`. */
26   - def value = t
27   - /** If there is evidence that `T` is a function type, then apply it. */
28   - def apply[F, G](f : F)(implicit ev : T <:< (F => G)) : G = t(f)
  26 +abstract class Case0Aux[-P, T] {
  27 + val value : T
29 28 }
30 29
31 30 /**
32   - * Base trait for polymorphic values.
  31 + * Type-specific case of a polymorphic unary function.
33 32 *
34 33 * @author Miles Sabin
35 34 */
36   -trait Poly {
37   - /** The common outer type constructor shared by all type-specific case of this polymorphic value. */
38   - type TC[_]
39   -
40   - /** The type of the `Case` representing this polymorphic value at type `TC[T]`. */
41   - type λ[T] = Case[this.type, TC[T]]
42   -
43   - /** Creates an instance of the `Case` representing this polymorphic value at type `TC[T]`. */
44   - def λ[T](c : TC[T]) = Case[this.type, TC[T]](c)
  35 +abstract class Case1Aux[-P, T] {
  36 + type R
  37 + val value : T => R
45 38 }
46 39
47 40 /**
48   - * Trait representing non-function polymorphic values.
  41 + * Type-specific case of a polymorphic binary function.
49 42 *
50 43 * @author Miles Sabin
51 44 */
52   -trait PolyVal[TC0[_]] extends Poly {
53   - type TC[X] = TC0[X]
54   - def apply[T](implicit c : λ[T]) : TC[T] = c.value
  45 +abstract class Case2Aux[-P, T, U] {
  46 + type R
  47 + val value : (T, U) => R
55 48 }
56 49
57 50 /**
58   - * Trait representing polymorphic function values.
  51 + * Base trait for polymorphic values.
59 52 *
60 53 * @author Miles Sabin
61 54 */
62   -trait HRFn extends Poly {
63   - /** The common form of this polymorphic function value in all type specific cases. */
64   - type TC[T] = F[T] => G[T]
  55 +trait Poly {
  56 + /** The type of the case representing this polymorphic value at type `T`. */
  57 + type Case0[T] = Case0Aux[this.type, T]
65 58
66   - /**
67   - * The common outer type constructor shared by the argument type of this polymorphic
68   - * function value in all type specific cases.
69   - * */
70   - type F[_]
71   - /**
72   - * The common outer type constructor shared by the result type of this polymorphic
73   - * function value in all type specific cases.
74   - * */
75   - type G[_]
76   -
77   - /** Implementation of the default case for this polymorphic function value */
78   - def default[T](f : F[T]) : G[T]
  59 + /** The type of the case representing this polymorphic unary function at argument type `T`. */
  60 + type Case1[T] = Case1Aux[this.type, T]
79 61
80   - /** The default case for this polymorphic function value */
81   - implicit def defaultCase[T] = λ[T](default)
  62 + /** The type of the case representing this polymorphic binary function at argument types `T` and `U`. */
  63 + type Case2[T, U] = Case2Aux[this.type, T, U]
82 64
83   - /** Apply this polymorphic function value using the appropriate type-specific case. */
84   - def apply[T](f : F[T])(implicit c : λ[T] = defaultCase[T]) : G[T] = c(f)
  65 + /** Creates an instance of the case representing this polymorphic value at type `T`. */
  66 + def case0[T](v : T) = new Case0[T] {
  67 + val value = v
  68 + }
  69 +
  70 + /** Creates an instance of the case representing this polymorphic unary function at argument type `T`. */
  71 + def case1[T] = new {
  72 + def apply[R0](f : T => R0) = new Case1[T] {
  73 + type R = R0
  74 + val value = f
  75 + }
  76 + }
  77 +
  78 + /** Creates an instance of the case representing this polymorphic binary function at argument types `T` and `U`. */
  79 + def case2[T, U] = new {
  80 + def apply[R0](f : (T, U) => R0) = new Case2[T, U] {
  81 + type R = R0
  82 + val value = f
  83 + }
  84 + }
  85 +
  86 + /** The type of a case of this polymorphic function of the form `T => T` */
  87 + type Hom[T] = Case1[T] { type R = T }
  88 +
  89 + /** The type of a case of this polymorphic function of the form `T => R` */
  90 + type Pullback1[T, R0] = Case1[T] { type R = R0 }
  91 +
  92 + /** The type of a case of this polymorphic function of the form `(T, U) => R` */
  93 + type Pullback2[T, U, R0] = Case2[T, U] { type R = R0 }
  94 +
  95 + def apply[T](implicit c : Case0[T]) : T = c.value
  96 + def apply[T](t : T)(implicit c : Case1[T]) : c.R = c.value(t)
  97 + def apply[T, U](t : T, u : U)(implicit c : Case2[T, U]) : c.R = c.value(t, u)
85 98 }
86 99
87 100 /**
88   - * Convenience trait for extension by polymorphic function value implementations.
89   - *
  101 + * Provides implicit conversions from polymorphic function values to monomorphic function values, eg. for use as
  102 + * arguments to ordinary higher order functions.
  103 + *
90 104 * @author Miles Sabin
91 105 */
92   -trait ~>[F0[_], G0[_]] extends HRFn {
93   - type F[X] = F0[X]
94   - type G[X] = G0[X]
  106 +object Poly {
  107 + implicit def inst0[P <: Poly, T](p : P)(implicit c : p.Case0[T]) : T = c.value
  108 + implicit def inst1[P <: Poly, T](p : P)(implicit c : p.Case1[T]) : T => c.R = c.value
  109 + implicit def inst2[P <: Poly, T, U](p : P)(implicit c : p.Case2[T, U]) : (T, U) => c.R = c.value
  110 +
  111 + type HomAux[-P, T] = Case1Aux[P, T] { type R = T }
  112 + type Pullback1Aux[-P, T, R0] = Case1Aux[P, T] { type R = R0 }
  113 + type Pullback2Aux[-P, T, U, R0] = Case2Aux[P, T, U] { type R = R0 }
95 114 }
96 115
97 116 /**
98   - * Mixin for polymorphic values which should have all their type specific cases covered by implicitly provided `Case`s
99   - * rather than by the default case.
  117 + * Base trait for natural transformations.
100 118 *
101 119 * @author Miles Sabin
102 120 */
103   -trait NoDefault extends HRFn {
104   - def default[T](f : F[T]) : G[T] = {
105   - sys.error("No default case for: "+getClass.getName+"@"+f.getClass.getName)
106   - }
  121 +trait ~>[F[_], G[_]] extends Poly {
  122 + def default[T](f : F[T]) : G[T]
  123 + def apply[T](f : F[T]) = default(f)
  124 + implicit def caseUniv[T] = case1[F[T]](default[T] _)
107 125 }
108 126
109   -/**
110   - * Provides an implicit conversion from polymorphic function values to monomorphic function values, eg. for use as
111   - * arguments to ordinary higher order functions.
112   - *
113   - * Includes miscellaneous polymorphic function definitions.
114   - *
115   - * @author Miles Sabin
116   - */
117   -object Poly {
118   - import TypeOperators._
119   -
120   - /**
121   - * Instantiates a polymorphic function value as an ordinary monomophic function value at a specific type.
122   - */
123   - implicit def univInstFn[HF <: HRFn, T](h : HF)(implicit c : h.λ[T] = h.defaultCase[T]) : h.TC[T] = c.value
124   -
125   - /** Polymorphic identity function. */
126   - object identity extends (Id ~> Id) {
127   - def default[T](t : T) = t
128   - }
  127 +object ~> {
  128 + implicit def inst1[G[_], T](p : Id ~> G) : T => G[T] = p.caseUniv[T].value
  129 + implicit def inst2[F[_], G[_], T](p : F ~> G) : F[T] => G[T] = p.caseUniv[T].value
  130 +}
129 131
130   - /** Polymorphic singleton function. */
131   - object singleton extends (Id ~> Set) {
132   - def default[T](t : T) = Set(t)
133   - }
  132 +/** Polymorphic identity function. */
  133 +object identity extends (Id ~> Id) {
  134 + def default[T](t : T) = t
  135 +}
134 136
135   - /** Polymorphic function selecting an arbitrary element from a non-empty `Set`. */
136   - object choose extends (Set ~> Option) {
137   - def default[T](s : Set[T]) = s.headOption
138   - }
  137 +/** Polymorphic singleton function. */
  138 +object singleton extends (Id ~> Set) {
  139 + def default[T](t : T) = Set(t)
  140 +}
139 141
140   - /** Polymorphic function creating singleton `List`s. */
141   - object list extends (Id ~> List) {
142   - def default[T](t : T) = List(t)
143   - }
144   -
145   - /** Polymorphic function returning the head of a `List`. */
146   - object headOption extends (List ~> Option) {
147   - def default[T](l : List[T]) = l.headOption
148   - }
149   -
150   - /** Polymorphic function testing whether or not an `Option` is defined. */
151   - object isDefined extends (Option ~> Const[Boolean]#λ) {
152   - def default[T](o : Option[T]) = o.isDefined
153   - }
154   -
155   - /** Polymorphic function which opens an `Option`. */
156   - object get extends (Option ~> Id) {
157   - def default[T](o : Option[T]) = o.get
158   - }
159   -
160   - /** Polymorphic function which injects a value into an `Option`. */
161   - object option extends (Id ~> Option) {
162   - def default[T](t : T) = Option(t)
163   - }
164   -
165   - /** Polymorphic zero with type specific cases. */
166   - object zero extends PolyVal[Id]
167   - implicit val intZero = zero.λ[Int](0)
168   - implicit val stringZero = zero.λ[String]("")
169   - implicit def listZero[T] = zero.λ[List[T]](Nil)
  142 +/** Polymorphic function selecting an arbitrary element from a non-empty `Set`. */
  143 +object choose extends (Set ~> Option) {
  144 + def default[T](s : Set[T]) = s.headOption
  145 +}
  146 +
  147 +/** Polymorphic function creating singleton `List`s. */
  148 +object list extends (Id ~> List) {
  149 + def default[T](t : T) = List(t)
  150 +}
  151 +
  152 +/** Polymorphic function returning the head of a `List`. */
  153 +object headOption extends (List ~> Option) {
  154 + def default[T](l : List[T]) = l.headOption
  155 +}
  156 +
  157 +/** Polymorphic function testing whether or not an `Option` is defined. */
  158 +object isDefined extends (Option ~> Const[Boolean]#λ) {
  159 + def default[T](o : Option[T]) = o.isDefined
  160 +}
  161 +
  162 +/** Polymorphic function which opens an `Option`. */
  163 +object get extends (Option ~> Id) {
  164 + def default[T](o : Option[T]) = o.get
  165 +}
  166 +
  167 +/** Polymorphic function which injects a value into an `Option`. */
  168 +object option extends (Id ~> Option) {
  169 + def default[T](t : T) = Option(t)
  170 +}
  171 +
  172 +/** Polymorphic addition with type specific cases. */
  173 +object plus extends Poly {
  174 + implicit val caseInt = case2[Int, Int](_ + _)
  175 + implicit val caseDouble = case2[Double, Double](_ + _)
  176 + implicit val caseString = case2[String, String](_ + _)
  177 + implicit def caseList[T] = case2[List[T], List[T]](_ ::: _)
  178 +}
  179 +
  180 +/** Polymorphic zero with type specific cases. */
  181 +object zero extends Poly {
  182 + implicit val zeroInt = case0[Int](0)
  183 + implicit val zeroDouble = case0[Double](0.0)
  184 + implicit val zeroString = case0[String]("")
  185 + implicit def zeroList[T] = case0[List[T]](Nil)
170 186 }
113 src/main/scala/shapeless/sybclass.scala
@@ -23,144 +23,155 @@ package shapeless
23 23 * @author Miles Sabin
24 24 */
25 25 object SybClass {
  26 + import Poly._
26 27 import TypeOperators._
27 28
28 29 /**
29 30 * Type class representing one-level generic queries.
30 31 */
31   - trait Data[HF <: HRFn, T] {
32   - def gmapQ(t : T) : List[HF#G[_]]
  32 + trait Data[F, T, R] {
  33 + def gmapQ(t : T) : List[R]
33 34 }
34 35
35   - def gmapQ[HF <: HRFn, T](f : HF)(t : T)(implicit data : Data[HF, T]) = data.gmapQ(t)
  36 + def gmapQ[F, T, R](f : F)(t : T)(implicit data : Data[F, T, R]) = data.gmapQ(t)
36 37
37 38 /**
38 39 * Default Data type class instance.
39 40 */
40   - implicit def dfltData[HF <: HRFn, T] = new Data[HF, T] {
41   - def gmapQ(t : T) : List[HF#G[_]] = Nil
  41 + implicit def dfltData[F, T, R] = new Data[F, T, R] {
  42 + def gmapQ(t : T) : List[R] = Nil
42 43 }
43 44
44 45 /**
45 46 * Data type class instance for pairs.
46 47 */
47   - implicit def pairData[HF <: HRFn, T, U](implicit qt : Case[HF, T => HF#G[_]], qu : Case[HF, U => HF#G[_]]) =
48   - new Data[HF, (T, U)] {
49   - def gmapQ(t : (T, U)) = List(qt(t._1), qu(t._2))
  48 + implicit def pairData[F <: Poly, T, U, R](implicit qt : Pullback1Aux[F, T, R], qu : Pullback1Aux[F, U, R]) =
  49 + new Data[F, (T, U), R] {
  50 + def gmapQ(t : (T, U)) = List(qt.value(t._1), qu.value(t._2))
50 51 }
51 52
52 53 /**
53 54 * Data type class instance for `Either`.
54 55 */
55   - implicit def eitherData[HF <: HRFn, T, U](implicit qt : Case[HF, T => HF#G[_]], qu : Case[HF, U => HF#G[_]]) =
56   - new Data[HF, Either[T, U]] {
  56 + implicit def eitherData[F <: Poly, T, U, R](implicit qt : Pullback1Aux[F, T, R], qu : Pullback1Aux[F, U, R]) =
  57 + new Data[F, Either[T, U], R] {
57 58 def gmapQ(t : Either[T, U]) = t match {
58   - case Left(t) => List(qt(t))
59   - case Right(u) => List(qu(u))
  59 + case Left(t) => List(qt.value(t))
  60 + case Right(u) => List(qu.value(u))
60 61 }
61 62 }
62 63
63 64 /**
64 65 * Data type class instance for `Option`.
65 66 */
66   - implicit def optionData[HF <: HRFn, T](implicit qt : Case[HF, T => HF#G[_]]) = new Data[HF, Option[T]] {
  67 + implicit def optionData[F <: Poly, T, R](implicit qt : Pullback1Aux[F, T, R]) = new Data[F, Option[T], R] {
67 68 def gmapQ(t : Option[T]) = t.map(qt.value).toList
68 69 }
69 70
70 71 /**
71 72 * Data type class instance for `List`s.
72 73 */
73   - implicit def listData[HF <: HRFn, T](implicit qt : Case[HF, T => HF#G[_]]) = new Data[HF, List[T]] {
  74 + implicit def listData[F <: Poly, T, R](implicit qt : Pullback1Aux[F, T, R]) = new Data[F, List[T], R] {
74 75 def gmapQ(t : List[T]) = t.map(qt.value)
75 76 }
76 77
77 78 /**
78 79 * Data type class instance for `HList`s.
79 80 */
80   - implicit def hlistData[HF <: HRFn, H, T <: HList](implicit qh : Case[HF, H => HF#G[_]], ct : Data[HF, T]) =
81   - new Data[HF, H :: T] {
82   - def gmapQ(t : H :: T) = qh(t.head) :: ct.gmapQ(t.tail)
  81 + implicit def hlistData[F <: Poly, H, T <: HList, R](implicit qh : Pullback1Aux[F, H, R], ct : Data[F, T, R]) =
  82 + new Data[F, H :: T, R] {
  83 + def gmapQ(t : H :: T) = qh.value(t.head) :: ct.gmapQ(t.tail)
83 84 }
84 85
85 86 /**
86 87 * Type class representing one-level generic transformations.
87 88 */
88   - trait DataT[HF, T] {
  89 + trait DataT[F, T] {
89 90 def gmapT(t : T) : T
90 91 }
91 92
92   - def gmapT[HF, T](f : HF)(t : T)(implicit data : DataT[HF, T]) = data.gmapT(t)
  93 + def gmapT[F, T](f : F)(t : T)(implicit data : DataT[F, T]) = data.gmapT(t)
93 94
94 95 /**
95 96 * Default DataT type class instance.
96 97 */
97   - implicit def dfltDataT[HF, T] : DataT[HF, T] = new DataT[HF, T] {
  98 + implicit def dfltDataT[F, T] : DataT[F, T] = new DataT[F, T] {
98 99 def gmapT(t : T) = t
99 100 }
100 101
101 102 /**
102 103 * DataT type class instance for pairs.
103 104 */
104   - implicit def pairDataT[HF, T, U](implicit ft : Case[HF, T => T], fu : Case[HF, U => U]) =
105   - new DataT[HF, (T, U)] {
106   - def gmapT(t : (T, U)) = (ft(t._1), fu(t._2))
  105 + implicit def pairDataT[F <: Poly, T, U](implicit ft : HomAux[F, T], fu : HomAux[F, U]) =
  106 + new DataT[F, (T, U)] {
  107 + def gmapT(t : (T, U)) = (ft.value(t._1), fu.value(t._2))
107 108 }
108 109
109 110 /**
110 111 * DataT type class instance for `Either`.
111 112 */
112   - implicit def eitherDataT[HF, T, U](implicit ft : Case[HF, T => T], fu : Case[HF, U => U]) =
113   - new DataT[HF, Either[T, U]] {
  113 + implicit def eitherDataT[F <: Poly, T, U](implicit ft : HomAux[F, T], fu : HomAux[F, U]) =
  114 + new DataT[F, Either[T, U]] {
114 115 def gmapT(t : Either[T, U]) = t match {
115   - case Left(t) => Left(ft(t))
116   - case Right(u) => Right(fu(u))
  116 + case Left(t) => Left(ft.value(t))
  117 + case Right(u) => Right(fu.value(u))
117 118 }
118 119 }
119 120
120 121 /**
121 122 * DataT type class instance for `Option`.
122 123 */
123   - implicit def optionDataT[HF, T](implicit ft : Case[HF, T => T]) = new DataT[HF, Option[T]] {
  124 + implicit def optionDataT[F <: Poly, T](implicit ft : HomAux[F, T] ) = new DataT[F, Option[T]] {
124 125 def gmapT(t : Option[T]) = t.map(ft.value)
125 126 }
126 127
127 128 /**
128 129 * DataT type class instance for `List`s.
129 130 */
130   - implicit def listDataT[HF, T](implicit ft : Case[HF, T => T]) = new DataT[HF, List[T]] {
  131 + implicit def listDataT[F <: Poly, T](implicit ft : HomAux[F, T]) = new DataT[F, List[T]] {
131 132 def gmapT(t : List[T]) = t.map(ft.value)
132 133 }
133 134
134 135 /**
135 136 * DataT type class instance for `HList`s.
136 137 */
137   - implicit def hlistDataT[HF <: HRFn, H, T <: HList](implicit fh : Case[HF, H => H], ct : DataT[HF, T]) =
138   - new DataT[HF, H :: T] {
139   - def gmapT(t : H :: T) = fh(t.head) :: ct.gmapT(t.tail)
  138 + implicit def hlistDataT[F <: Poly, H, T <: HList](implicit fh : HomAux[F, H], ct : DataT[F, T]) =
  139 + new DataT[F, H :: T] {
  140 + def gmapT(t : H :: T) = fh.value(t.head) :: ct.gmapT(t.tail)
140 141 }
141 142
142   - case class Node[T](t : T, c : List[Node[T]] = Nil) {
143   - def fold(f : (T, T) => T) : T = c.map(_.fold(f)).foldLeft(t)(f)
  143 + /** The SYB everything combinator */
  144 + type Everything[F <: Poly, K <: Poly, T] = Case1Aux[EverythingAux[F, K], T]
  145 +
  146 + class EverythingAux[F <: Poly, K <: Poly] extends Poly
  147 +
  148 + object EverythingAux {
  149 + implicit def default[F <: Poly, K <: Poly, T, R0]
  150 + (implicit f : Pullback1Aux[F, T, R0], data : Data[EverythingAux[F, K], T, R0], k : Pullback2Aux[K, R0, R0, R0]) =
  151 + new Case1Aux[EverythingAux[F, K], T] {
  152 + type R = R0
  153 + val value : T => R = (t => data.gmapQ(t).foldLeft(f.value(t))(k.value))
  154 + }
144 155 }
145 156
146   - type Everything[HF <: HRFn, T] = Case[Everything0[HF], T => Node[HF#G[_]]]
147   -
148   - trait Everything0[HF <: HRFn] extends (Id ~> Const[Node[HF#G[_]]]#λ) with NoDefault
  157 + class ApplyEverything[F <: Poly] {
  158 + def apply[K <: Poly](k : K) = new EverythingAux[F, K]
  159 + }
149 160
150   - /** The SYB everything combinator */
151   - def everything[HF <: HRFn, T](f : HF)(k : (HF#G[_], HF#G[_]) => HF#G[_])(t : T)
152   - (implicit c : Case[Everything0[HF], T => Node[HF#G[_]]]) : HF#G[_] = c(t).fold(k)
153   -
154   - implicit def everythingDflt[HF <: HRFn, T](implicit data : Data[Everything0[HF], T], fT : Case[HF, T => HF#G[_]]) =
155   - new Case[Everything0[HF], T => Node[HF#G[_]]](t => Node(fT(t), data.gmapQ(t)))
156   -
157   - type Everywhere[HF, T] = Case[Everywhere0[HF], T => T]
158   -
159   - trait Everywhere0[HF] extends (Id ~> Id) with NoDefault
160   -
  161 + def everything[F <: Poly](f : F) = new ApplyEverything[F]
  162 +
161 163 /** The SYB everywhere combinator */
162   - def everywhere[HF <: HRFn](f : HF) = new Everywhere0[f.type] {}
  164 + type Everywhere[F <: Poly, T] = HomAux[EverywhereAux[F], T]
  165 +
  166 + class EverywhereAux[F <: Poly] extends Poly
  167 +
  168 + object EverywhereAux {
  169 + implicit def default[F <: Poly, T](implicit data : DataT[EverywhereAux[F], T], f : HomAux[F, T]) =
  170 + new Case1Aux[EverywhereAux[F], T] {
  171 + type R = T
  172 + val value : T => R = t => f.value(data.gmapT(t))
  173 + }
  174 + }
163 175
164   - implicit def everywhereDflt[HF, T](implicit data : DataT[Everywhere0[HF], T], fT : Case[HF, T => T]) =
165   - new Case[Everywhere0[HF], T => T](t => fT(data.gmapT(t)))
  176 + def everywhere[F <: Poly](f : F) = new EverywhereAux[f.type]
166 177 }
1  src/test/scala/shapeless/conversions.scala
@@ -23,7 +23,6 @@ class ConversionTests {
23 23 import Tuples._
24 24 import Functions._
25 25 import HList._
26   - import Poly._
27 26
28 27 def typed[T](t : => T) {}
29 28
4 src/test/scala/shapeless/hlist.scala
@@ -22,7 +22,6 @@ import org.junit.Assert._
22 22 class HListTests {
23 23 import HList._
24 24 import Typeable._
25   - import Poly._
26 25 import Traversables._
27 26 import Nat._
28 27 import Tuples._
@@ -88,8 +87,7 @@ class HListTests {
88 87 @Test
89 88 def testMap {
90 89 implicitly[MapperAux[choose.type, HNil, HNil]]
91   - implicitly[Case[choose.type, Set[Int] => Option[Int]]]
92   - implicitly[choose.λ[Int]]
  90 + implicitly[choose.Case1[Set[Int]]]
93 91 implicitly[MapperAux[choose.type, Set[Int] :: HNil, Option[Int] :: HNil]]
94 92
95 93 val s1 = Set(1) :: HNil
1  src/test/scala/shapeless/lift.scala
@@ -20,7 +20,6 @@ import org.junit.Test
20 20 import org.junit.Assert._
21 21
22 22 class LiftTests {
23   - import Poly._
24 23 import HList._
25 24 import Tuples._
26 25 import Functions._
110 src/test/scala/shapeless/poly.scala
@@ -21,7 +21,6 @@ import org.junit.Assert._
21 21
22 22 class PolyTests {
23 23 import TypeOperators._
24   - import Poly._
25 24
26 25 def typed[T](t : => T) {}
27 26
@@ -29,30 +28,24 @@ class PolyTests {
29 28 def default[T](t : T) = t.toString.toInt
30 29 }
31 30
32   - object size extends (Id ~> Const[Int]#λ) {
33   - def default[T](t : T) = 1
  31 + object size extends Poly {
  32 + implicit def default[T] = case1[T](t => 1)
  33 + implicit def caseInt = case1[Int](x => 1)
  34 + implicit def caseString = case1[String](_.length)
  35 + implicit def caseList[T] = case1[List[T]](_.length)
  36 + implicit def caseOption[T](implicit st : Pullback1[T, Int]) = case1[Option[T]](t => 1+(t map size).getOrElse(0))
  37 + implicit def caseTuple[T, U](implicit st : Pullback1[T, Int], su : Pullback1[U, Int]) = case1[(T, U)](t => size(t._1)+size(t._2))
34 38 }
35   - implicit def sizeInt = size.λ[Int](x => 1)
36   - implicit def sizeString = size.λ[String](s => s.length)
37   - implicit def sizeList[T] = size.λ[List[T]](l => l.length)
38   - implicit def sizeOption[T](implicit cases : size.λ[T]) = size.λ[Option[T]](t => 1+(t map size).getOrElse(0))
39   - implicit def sizeTuple[T, U](implicit st : size.λ[T], su : size.λ[U]) = size.λ[(T, U)](t => size(t._1)+size(t._2))
40 39
41 40 @Test
42 41 def testHRFn {
43   - implicitly[choose.λ[Int]]
44   - implicitly[Case[choose.type, Set[Int] => Option[Int]]]
  42 + implicitly[choose.Case1[Set[Int]]]
45 43
46   - implicitly[size.λ[Int]]
47   - implicitly[Case[size.type, Option[Int] => Int]]
  44 + implicitly[size.Case1[Int]]
48 45
49   - implicitly[option.λ[Int]]
50   - implicitly[Case[option.type, Int => Option[Int]]]
51   - implicitly[Case[option.type, Id[Int] => Option[Int]]]
  46 + implicitly[option.Case1[Int]]
52 47
53   - implicitly[singleton.λ[Int]]
54   - implicitly[Case[singleton.type, Int => Set[Int]]]
55   - implicitly[Case[singleton.type, Id[Int] => Set[Int]]]
  48 + implicitly[singleton.Case1[Int]]
56 49
57 50 val si = size(23)
58 51 assertEquals(1, si)
@@ -116,7 +109,7 @@ class PolyTests {
116 109
117 110 // Use as polymorphic function values
118 111 def pairApply[G[_]](f : Id ~> G) = (f(23), f("foo"))
119   -
  112 +
120 113 val a1 = pairApply(singleton)
121 114 typed[(Set[Int], Set[String])](a1)
122 115 assertEquals((Set(23), Set("foo")), a1)
@@ -124,13 +117,9 @@ class PolyTests {
124 117 val a2 = pairApply(list)
125 118 typed[(List[Int], List[String])](a2)
126 119 assertEquals((List(23), List("foo")), a2)
127   -
128   - val a3 = pairApply[Const[Int]#λ](size)
129   - typed[(Int, Int)](a3)
130   - assertEquals((1, 1), a3) // size without type specific cases
131   -
  120 +
132 121 // Use as polymorphic function values with type specific cases
133   - def pairApply2[G[_]](f : Id ~> G)(implicit fi : f.λ[Int], fs : f.λ[String]) = (f(23), f("foo"))
  122 + def pairApply2[F <: Poly](f : F)(implicit ci : f.Case1[Int], cs : f.Case1[String]) = (f(23), f("foo"))
134 123
135 124 val a4 = pairApply2(singleton)
136 125 typed[(Set[Int], Set[String])](a4)
@@ -140,12 +129,12 @@ class PolyTests {
140 129 typed[(List[Int], List[String])](a5)
141 130 assertEquals((List(23), List("foo")), a5)
142 131
143   - val a6 = pairApply2[Const[Int]#λ](size)
  132 + val a6 = pairApply2(size)
144 133 typed[(Int, Int)](a6)
145 134 assertEquals((1, 3), a6)
146 135
147   - def pairMap[F[_]](f : Id ~> F) = (List(1, 2, 3) map f, List("foo", "bar", "baz") map f)
148   -
  136 + def pairMap[G[_]](f : Id ~> G) = (List(1, 2, 3) map f, List("foo", "bar", "baz") map f)
  137 +
149 138 val m1 = pairMap(singleton)
150 139 typed[(List[Set[Int]], List[Set[String]])](m1)
151 140 assertEquals((List(Set(1), Set(2), Set(3)), List(Set("foo"), Set("bar"), Set("baz"))), m1)
@@ -169,11 +158,24 @@ class PolyTests {
169 158 assertEquals(List(Option(1), Option(2), Option(3)), loi2)
170 159
171 160 import HList._
  161 + import Mapper._
  162 + import MapperAux._
172 163
173 164 val l8 = 23 :: "foo" :: List(1, 2, 3, 4) :: Option("bar") :: (23, "foo") :: 2.0 :: HNil
174 165 val l9 = l8 map size
175 166 typed[Int :: Int :: Int :: Int :: Int :: Int :: HNil](l9)
176 167 assertEquals(1 :: 3 :: 4 :: 4 :: 4 :: 1 :: HNil, l9)
  168 +
  169 + def hlistMap[F <: Poly](f : F)(implicit mapper : Mapper[F, Int :: String :: HNil]) =
  170 + (23 :: "foo" :: HNil) map f
  171 +
  172 + val hm1 = hlistMap(singleton)
  173 + typed[Set[Int] :: Set[String] :: HNil](hm1)
  174 + assertEquals(Set(23) :: Set("foo") :: HNil, hm1)
  175 +
  176 + val hm2 = hlistMap(list)
  177 + typed[List[Int] :: List[String] :: HNil](hm2)
  178 + assertEquals(List(23) :: List("foo") :: HNil, hm2)
177 179 }
178 180
179 181 @Test
@@ -203,52 +205,28 @@ class PolyTests {
203 205 assertEquals(List(23), l2)
204 206 }
205 207
  208 + // Polymophic function value with type-specific cases for two
  209 + // argument types. Result type is dependent on argument type
  210 + object bidi extends Poly {
  211 + implicit val caseInt = case1[Int](_.toString)
  212 + implicit val caseString = case1[String](_.toInt)
  213 + }
  214 +
206 215 @Test
207 216 def testCompose {
208 217 import Typeable._
209 218
210   - // Polymophic function value with type-specific cases for two
211   - // argument types. Any is the common result type.
212   -
213   - object bidi extends (Id ~> Any) with NoDefault
214   - implicit val bidiInt = bidi.λ[Int](_.toString)
215   - implicit val bidiString = bidi.λ[String](_.toInt)
216   -
217   - val bi = bidi(23) // type is Any
  219 + val bi = bidi(23)
  220 + typed[String](bi)
218 221 assertEquals("23", bi)
219   - val bs = bidi("23") // type is Any
  222 +
  223 + val bs = bidi("23")
  224 + typed[Int](bs)
220 225 assertEquals(23, bs)
221 226
222 227 val lis = 1 :: "2" :: 3 :: "4" :: HNil
223   - val blis = lis map bidi // type is Any :: Any :: Any :: Any :: HNil
  228 + val blis = lis map bidi
  229 + typed[String :: Int :: String :: Int :: HNil](blis)
224 230 assertEquals("1" :: 2 :: "3" :: 4 :: HNil, blis)
225   -
226   - // The common result type means we've lost precision. We can regain it
227   - // with a cast (see below for a solution which avoids this problem.
228   - val oblis = blis.cast[String :: Int :: String :: Int :: HNil]
229   - typed[Option[String :: Int :: String :: Int :: HNil]](oblis)
230   - assertTrue(oblis.isDefined)
231   - assertEquals("1" :: 2 :: "3" :: 4 :: HNil, oblis.get)
232   -
233   - // Shapeless doesn't currently have direct support for making the result
234   - // type an ad hoc function of the argument type. Nevertheless we can
235   - // reuse most of the polymorphic value infrastructure.
236   - object bidi2 {
237   - def apply[T, U](t : T)(implicit c : Case[this.type, T => U]) : U = c(t)
238   - }
239   - implicit val bidi2Int = Case[bidi2.type, Int => String](_.toString)
240   - implicit val bidi2String = Case[bidi2.type, String => Int](_.toInt)
241   -
242   - val bi2 = bidi2(23) // type is String
243   - typed[String](bi2)
244   - assertEquals("23", bi)
245   - val bs2 = bidi2("23") // type is Int
246   - typed[Int](bs2)
247   - assertEquals(23, bs)
248   -
249   - val lis2 = 1 :: "2" :: 3 :: "4" :: HNil
250   - val blis2 = lis2 map bidi2 // type is String :: Int :: String :: Int :: HNil
251   - typed[String :: Int :: String :: Int :: HNil](blis2)
252   - assertEquals("1" :: 2 :: "3" :: 4 :: HNil, blis2)
253 231 }
254 232 }
47 src/test/scala/shapeless/sybclass.scala
@@ -22,34 +22,35 @@ class SybClassTests {
22 22
23 23 import SybClass._
24 24 import TypeOperators._
25   - import Poly._
26 25 import HList._
27 26
28 27 def typed[T](t : => T) {}
29 28
30   - object gsizeAll extends (Id ~> Const[Int]#λ) with NoDefault
31   - implicit def gsizeAllString = gsizeAll.λ[String](s => s.length)
32   - implicit def gsizeAllDflt[T](implicit data : Data[gsizeAll.type, T]) = gsizeAll.λ[T](1+data.gmapQ(_).sum)
  29 + object gsizeAll extends Poly {
  30 + implicit def caseString = case1[String](_.length)
  31 + implicit def default[T](implicit data : Data[this.type, T, Int]) = case1[T](1+data.gmapQ(_).sum)
  32 + }
33 33
34   - object gsize extends (Id ~> Const[Int]#λ) {
35   - def default[T](t : T) = 1
  34 + object gsize extends Poly {
  35 + implicit def caseInt = case1[Int](i => 1)
  36 + implicit def caseString = case1[String](_.length)
  37 + implicit def default[T] = case1[T](t => 1)
36 38 }
37   - implicit def gsizeInt = gsize.λ[Int](i => 1)
38   - implicit def gsizeString = gsize.λ[String](s => s.length)
39   -
40   - def gsizeAll2[T](t : T)(implicit e : Everything[gsize.type, T]) : Int = everything(gsize)(_+_)(t)
41 39
42   - object incAll extends (Id ~> Id) with NoDefault
43   - implicit def incAllInt = incAll.λ[Int](_+1)
44   - implicit def incAllString = incAll.λ[String](_+"*")
45   - implicit def incAllDflt[T](implicit data : DataT[incAll.type, T]) = incAll.λ[T](data.gmapT)
  40 + def gsizeAll2[T](t : T)(implicit e : Everything[gsize.type, plus.type, T]) = everything(gsize)(plus)(t)
46 41
47   - object inc extends (Id ~> Id) {
48   - def default[T](t : T) = t
  42 + object incAll extends Poly {
  43 + implicit def caseInt = case1[Int](_+1)
  44 + implicit def caseString = case1[String](_+"*")
  45 + implicit def default[T](implicit data : DataT[this.type, T]) = case1[T](data.gmapT)
49 46 }
50   - implicit def incInt = inc.λ[Int](_+1)
51   - implicit def incString = inc.λ[String](_+"*")
52 47
  48 + object inc extends Poly {
  49 + implicit def caseInt = case1[Int](_+1)
  50 + implicit def caseString = case1[String](_+"*")
  51 + implicit def default[T] = case1[T](t => t)
  52 + }
  53 +
53 54 def incAll2[T](t : T)(implicit e : Everywhere[inc.type, T]) : T = everywhere(inc)(t)
54 55
55 56 @Test
@@ -88,19 +89,19 @@ class SybClassTests {
88 89
89 90 @Test
90 91 def testEverything {
91   - val e1 = everything(gsize)(_+_)(23)
  92 + val e1 = everything(gsize)(plus)(23)
92 93 typed[Int](e1)
93 94 assertEquals(1, e1)
94 95
95   - val e2 = everything(gsize)(_+_)("foo")
  96 + val e2 = everything(gsize)(plus)("foo")
96 97 typed[Int](e2)
97 98 assertEquals(3, e2)
98 99
99   - val e3 = everything(gsize)(_+_)((23, "foo"))
  100 + val e3 = everything(gsize)(plus)((23, "foo"))
100 101 typed[Int](e3)
101 102 assertEquals(5, e3)
102 103
103   - val e4 = everything(gsize)(_+_)(List(1, 2, 3, 4))
  104 + val e4 = everything(gsize)(plus)(List(1, 2, 3, 4))
104 105 typed[Int](e4)
105 106 assertEquals(5, e4)
106 107
@@ -184,7 +185,7 @@ class SybClassTests {
184 185 typed[Int :: String :: Boolean :: Double :: HNil](li)
185 186 assertEquals(24 :: "foo*" :: true :: 2.0 :: HNil, li)
186 187
187   - val ls = everything(gsize)(_+_)(l)
  188 + val ls = everything(gsize)(plus)(l)
188 189 typed[Int](ls)
189 190 assertEquals(7, ls)
190 191 }

0 comments on commit 447aae4

Jason Zaugg

Might it be useful to constrain R0 with an upper (and maybe a lower) bound, specified as a type member of Poly? The bidi example is cute, but the in the common case of a uniform return type it would be nice to be explicit and get an earlier type error.

Jason Zaugg

BTW, doesn't this lead to use of a structural type? I would use a named class, instead of new { ... }

Miles Sabin

I tried a few things along those lines, but couldn't make it work ... let me know if you come up with anything.

You can express the requirement of a common result type at use sites by asking for evidence of the form f.Pullback1[T, R] (ie. for any argument type T the result is of a fixed type R). This is used in the SYB module and works out quite nicely ... but clearly it doesn't prevent someone from defining additional cases which have different result types. It'd be very desirable to block that at source ... I'd do it if I knew how.

On the structural type: yes, but it's one-off and transient ... I should probably get rid of it, but it doesn't feel super urgent.

Jason Zaugg

Hmmm. So I tried the obvious thing first:

trait Poly {
  /** The type of the case representing this polymorphic value at type `T`. */
  type Case0[T] = Case0Aux[this.type, T]

  /** The type of the case representing this polymorphic unary function at argument type `T`. */
  type Case1[T] = Case1Aux[this.type, T]

  /** The type of the case representing this polymorphic binary function at argument types `T` and `U`. */
  type Case2[T, U] = Case2Aux[this.type, T, U]

  type U <: Any

  /** Creates an instance of the case representing this polymorphic value at type `T`. */
  def case0[T](v : T) = new Case0[T] {
    val value = v
  }

  /** Creates an instance of the case representing this polymorphic unary function at argument type `T`. */
  def case1[T] = new {
    def apply[R0 <: U](f : T => R0) = new Case1[T] {
      type R = R0
      val value = f
    }
  }
}

object toDouble extends Poly {
  type U = Double
  implicit def caseInt = case1[Int](_.toDouble)
  implicit def caseDouble = case1[Double](x => x)
}

So far, so good, it won't allow non-double returning cases.

But leave U unspecified and:

[error] C:\code\analytics\analytics-core\src\main\scala\com\efgfp\analytics\core\method\PricingRequest.scala:106: inferred type arguments [Double] do not conform to method apply's type parameter bounds [R0 <: com.efgfp.analytics.core.method.toDouble.U]
[error]   implicit def caseInt = case1[Int](_.toDouble)

It's a bit awkward. How about providing two versions?

trait PolyFixedReturn[R] extends Poly { type U = R }
trait PolyVariableReturn extends Poly { type U = Any }
Miles Sabin

Structural types removed in latest commit. I'm having another think about case restrictions.

Miles Sabin

Definition site constraints on case shapes supported in the latest commit. For your scenarios you'll want to extend Pullback1[R] to limit the case result types uniformly to R. Let me know how you get on ...

Please sign in to comment.
Something went wrong with that request. Please try again.