I'm trying to figure out how to associate a feature (singleton type) of shapeless with another (HList), assuming we would like to derive a generic type Vector
that contains information about the arity of the input vector:
package com.tribbloids.spike.shapeless_spike.shapesafe
import shapeless.{Generic, Witness}
import scala.language.implicitConversions
trait Vector[W <: Witness.Lt[Int]] extends Serializable {
def witness: W
def <*>(that: Vector[W]): Vector[W] = ???
}
object Vector {
case class Impl[W <: Witness.Lt[Int]](witness: W) extends Vector[W] {}
def zeros[W <: Witness.Lt[Int]](witness: W): Impl[W] = Impl(witness)
object Attempt1 {
def values(v: (Double)) = Impl(Witness(1))
def values(v: (Double, Double)) = Impl(Witness(2))
def values(v: (Double, Double, Double)) = Impl(Witness(3))
Vector.zeros(Witness(2)) <*> values(2.0, 3.0)
Vector.zeros(Witness(3)) <*> values(2.0, 3.0) // breaks as expected
}
}
I can write numerous lines to support the derivation (which will eventually hit the JVM's limitation for classloading), or I can write a more succinct implicit conversion to dynamically crate singleton types on-demand, something like this:
object Attempt2 {
type HInts = Int :: HInts
def values[T <: Product](v: T)(implicit aux: Generic.Aux[T, HInts]) = {
???
}
}
However, despite that both are very matured shapeless feature, it seems to have no documentation teaching how to enable such algebraic type derivation.
Is there an easy way to replace all my implicit functions with a general enough statement? I'm using scala-2.12 + shapeless 2.3 at the moment.
Thanks a lot for the information.
If all you need is
it is much easier, if you use Nat
instead of Witness.Lt[Int]
:
~ amm
Loading...
Welcome to the Ammonite Repl 2.0.4 (Scala 2.13.1 Java 1.8.0_242)
@ import $ivy.`com.chuusai::shapeless:2.3.3`, shapeless._
import $ivy.$ , shapeless._
@ {
trait OfSize[A, N <: Nat]
object OfSize {
implicit def evidence[A, Repr <: HList, N <: Nat](
implicit gen: Generic.Aux[A, Repr],
length: shapeless.ops.hlist.Length.Aux[Repr, N]
): OfSize[A, N] = new OfSize[A, N] {}
}
}
defined trait OfSize
defined object OfSize
@ def needSized[A, N <: Nat](a: A, n: N)(implicit ev: A OfSize N) = "OK"
defined function needSized
@ case class Test(a: String, b: Int)
defined class Test
@ needSized(Test("a", 0), Nat(3))
cmd4.sc:1: could not find implicit value for parameter ev: ammonite.$sess.cmd1.OfSize[ammonite.$sess.cmd3.Test,shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]
val res4 = needSized(Test("a", 0), Nat(3))
^
Compilation Failed
@ needSized(Test("a", 0), Nat(2))
res4: String = "OK"
@
I believe Nat
in some for will be available in Shapeless 3, so it would be more portable approach than relying on witnesses. If you wanted to use Witness, however, then I believe then it would be required to provide your own macro to convert Witness to Nat and then use existing shapeless utilities.