-
-
Save mergeconflict/2188596 to your computer and use it in GitHub Desktop.
object AllExamples extends App { | |
import shapeless._ | |
final class All[L <: HList](val values: L) { | |
def apply[A](implicit selector: Selector[L, A]): A = values.select[A] | |
} | |
object All { | |
// package a value of type A, convertible to type B, into an HList containing just B | |
implicit def toAll0[A, B](a: A)(implicit a2b: A => B): All[B :: HNil] = | |
new All(a :: HNil) | |
// package a value of type A, convertible to B and some other types L, into an HList containing B and all types in L | |
implicit def toAll1[A, B, L <: _ :: _](a: A)(implicit a2b: A => B, a2l: A => All[L]): All[B :: L] = | |
new All(a :: a.values) | |
} | |
// example data | |
trait A { def a: String } | |
trait B { def b: String } | |
trait C { def c: String } | |
implicit def intA(n: Int): A = new A { def a = "a " + n.toString } | |
implicit def intB(n: Int): B = new B { def b = "b " + n.toString } | |
implicit def intC(n: Int): C = new C { def c = "c " + n.toString } | |
implicit def stringA(s: String): A = new A { def a = "a " + s } | |
implicit def stringB(s: String): B = new B { def b = "b " + s } | |
implicit def stringC(s: String): C = new C { def c = "c " + s } | |
// implicitly convert int and string values to A, B and Cs | |
val abcs: Seq[All[A :: B :: C :: HNil]] = Seq(1, "haha", 2, "hoho") | |
// unpack | |
for (abc <- abcs) { | |
println(abc[A].a) | |
println(abc[B].b) | |
println(abc[C].c) | |
} | |
} |
Yes. The problem is that All.apply[T] is only applicable as a conversion from abc to A once we have inferred that T is A. But the compiler will only attempt that inference if it thinks that All.apply[T] is applicable. Chicken, meet egg. Egg, chicken. :-(
That'd be great :-)
A couple of things first ... could you put an Apache 2 license header at the top (copy one from one of the other shapeless files). Also, I think you can change "L <: _ :: _" @ line 15 to "L <: HList". I might make a few minor tweaks once it's in.
Hmmm, I'm not sure I understand your explanation about conversions exactly. I didn't think there were any actual conversions taking place at the time when we're trying to unpack All
. Did you mean that it can't resolve the implicit selector: Selector[L, A]
evidence until it's inferred a binding for A
, and it can't infer a binding for A
until it's resolved the implicit?
I noticed that the way you solved that chicken/egg problem in your Pack
example was to provide unpack
as an implicit member which you import at the use site. This works I guess because there's only one typeclass there (i.e. the F
in Pack[F[_], L <: HList]
)? That is, if you wanted to pack an arbitrary number of typeclass instances you wouldn't be able to do the same trick, right?
Yes, exactly that. Sorry I confused things by mentioning conversions ... I was playing around with something which tried to use a conversion from All[... T ...] to T which got bitten by exactly the same problem.
In my Pack case things are a bit different: the additional structure in the result type (due to the outer type constructor) seems to be enough to allow the inference/resolution to go through. I think having a single argument list with multiple Packs at different type constructors will probably work, but I haven't checked.
Also BTW, I think I've figured out a way to sugar
All[A :: B :: C :: HNil]
into the syntax I'd wanted before,A & B & C
. I could submit a pull request with all this stuff, to add to your examples, if you're interested...