I attempted to write a typeclass, SumEq5
, such that its HList
type parameter's first two fields add up to 5
:
trait SumEq5[A]
object SumEq5 {
def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev
implicit def sumEq5Ev[L <: HList, A <: Nat, B <: Nat](
implicit hcons: IsHCons.Aux[L, A, B :: HNil],
ev: Sum.Aux[A, B, _5]
): SumEq5[L] = new SumEq5[L] {}
}
But it doesn't appear to work:
import shapeless._
import shapeless.nat._
import net.SumEq5
scala> SumEq5[_0 :: _5 :: HNil]
<console>:19: error: could not find implicit value for
parameter ev: net.SumEq5[shapeless.::[shapeless.nat._0,shapeless.::
[shapeless.nat._5,shapeless.HNil]]]
SumEq5[_0 :: _5 :: HNil]
Please give me a hint as to why _0 :: _5 :: HNil
does not have evidence that its two Nat
's are equal to 5.
EDIT
Updated question per Denis Rosca's help in shapeless's gitter .
I only have a partial answer for you, ie a (workaround) solution without understanding why exactly the original doesn't work as intended.
Seems you can't ask directly for a IsHCons.Aux[L, A, B :: HNil]
, you need to do it piecemeal:
IsHCons.Aux[L, A, L2]
, and then IsHCons.Aux[L2, B, HNil]
Therefore, this compiles:
import shapeless._, nat._, ops.hlist._, ops.nat._
trait SumEq5[A]
object SumEq5 {
def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev
implicit def sumEq5Ev[L <: HList, L2 <: HList, A <: Nat, B <: Nat](
implicit hcons0: IsHCons.Aux[L, A, L2],
hcons: IsHCons.Aux[L2, B, HNil],
ev: Sum.Aux[A, B, _5]
): SumEq5[L] = new SumEq5[L] {}
}
object T {
def main(args: Array[String]): Unit = {
SumEq5[_0 :: _5 :: HNil]
}
}
Following from Miles Sabin's answer , this can be tweaked to support any HList of 2 or more elements, of which the sum of the first two is 5, like so:
import shapeless._, nat._, ops.hlist._, ops.nat._
trait SumEq5[A]
object SumEq5 {
def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev
implicit def sumEq5Ev[L1 <: HList, L2 <: HList, L3 <: HList, A <: Nat, B <: Nat](
implicit hcons1: IsHCons.Aux[L1, A, L2],
hcons2: IsHCons.Aux[L2, B, L3],
ev: Sum.Aux[A, B, _5]
): SumEq5[L1] = new SumEq5[L1] {}
}
object T {
def main(args: Array[String]): Unit = {
SumEq5[_0 :: _5 :: HNil]
}
}
Dale Wijnand and Marcus Henry are pointing in the right direction if you want to generalize to HList
s of arbitrary length, however if you really only want to accommodate two element HList
s, then the following is a rather simpler solution,
scala> import shapeless._, nat._, ops.nat._
import shapeless._
import nat._
import ops.nat._
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait SumEq5[A]
object SumEq5 {
def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev
implicit def sumEq5AB[A <: Nat, B <: Nat]
(implicit ev: Sum.Aux[A, B, _5]): SumEq5[A :: B :: HNil] =
new SumEq5[A :: B :: HNil] {}
}
// Exiting paste mode, now interpreting.
defined trait SumEq5
defined object SumEq5
scala> SumEq5[_0 :: _5 :: HNil]
res0: SumEq5[_0 :: _5 :: HNil]] = SumEq5$$anon$1@658c5e59
The main difference here is that the instance is explicitly defined for two element lists rather than being defined for lists in general with the proviso that there exists a proof that the list has exactly two elements.
Following Dale's update, we can generalize this to accommodate HList
s with at least two (rather than exactly two) elements, again without any additional witnesses,
scala> import shapeless._, nat._, ops.nat._
import shapeless._
import nat._
import ops.nat._
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait SumEq5[A]
object SumEq5 {
def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev
implicit def sumEq5AB[A <: Nat, B <: Nat, T <: HList]
(implicit ev: Sum.Aux[A, B, _5]): SumEq5[A :: B :: T] =
new SumEq5[A :: B :: T] {}
}
// Exiting paste mode, now interpreting.
defined trait SumEq5
defined object SumEq5
scala> SumEq5[_0 :: _5 :: HNil]
res0: SumEq5[_0 :: _5 :: HNil]] = SumEq5$$anon$1@658c5e59
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.