无形:检查多态函数的类型约束

Kli*_*nke 7 scala hlist shapeless

我正在研究一个经济模型的小型库,它使用类型来检查实体的单位,例如,而不是val apples = 2.0我们写的val apples = GoodsAmount[KG, Apples](2.0).为了创建捆绑商品,我尝试使用无形库中的HLists.这工作正常,但在某些情况下,我不能像我喜欢的那样通用代码.参见例如以下问题.

我从一个简单的代码开始,解释我想要变成什么样的形状.我们创建两个类,代表Km,其他Miles.应该允许添加Km类,但不能增加里程.我使用抽象类型T的主要动机是我们更复杂的库.对'+'函数的间接调用只是因为我们需要在无形状的情况下类似的东西.

trait Foo {
  type T
  val v: Double
  def +[B <: Foo](other: B)(implicit ev: this.T =:= other.T) = v + other.v
}

trait _Km 
trait _Miles

case class Km(v: Double)    extends Foo { type T = _Km }
case class Miles(v: Double) extends Foo { type T = _Miles }

object ExampleSimple extends App {
  def add[A <: Foo, B <: Foo](a: A, b: B)(implicit ev: a.T =:= b.T) = { a + b }

  add(Km(1), Km(2))
  // add(Km(1), Miles(2)) /* does not compile as intended */
}
Run Code Online (Sandbox Code Playgroud)

这按预期工作.但是必须在"添加"功能上检查Type Contraint.我将此扩展到HLists的尝试如下:

object ExampleShapeless extends App {
  import shapeless._

  val l1 = Km(1) :: Km(2) :: HNil
  val l2 = Km(4) :: Km(3) :: HNil

  object add extends Poly1 {
    implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a, b) => a + b }
  }

  (l1 zip l2).map(add)
}
Run Code Online (Sandbox Code Playgroud)

但是这会生成以下错误消息(使用Scala 2.10.2):

[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:50: Cannot prove that a.T =:= b.T.
[error]     implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a: Foo, b) => a + b }
[error]                                                                          ^
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:54: could not find implicit value for parameter mapper: shapeless.Mapper[ExampleShapeless.add.type,shapeless.::[(Km, Km),shapeless.::[(Km, Km),shapeless.HNil]]]
[error]   (l1 zip l2).map(add)
Run Code Online (Sandbox Code Playgroud)

在我可以向caseTuple函数添加类型约束的情况下,应该修复第一个错误,但说实话,我还没有理解at函数是如何工作的以及我可以在哪里添加隐式证据参数.我也不知道,我必须做什么,以便Mapper找到他的隐含价值.

一个不太通用的版本,我将caseTuple函数替换为

implicit def caseTuple = at[(Km,Km)] { case (a, b) => a + b }
Run Code Online (Sandbox Code Playgroud)

工作正常,但需要编写大量冗余代码(好吧,这个解决方案仍然会更好,因为我们目前的解决方案使用元组).有人能给我一个提示,我怎么能解决这个问题?

谢谢,克林克

Tra*_*own 7

您可以通过向案例添加类型参数来要求类型成员匹配:

object add extends Poly1 {
  implicit def caseTuple[_T, A <: Foo { type T = _T }] = at[(A, A)] {
    case (a, b) => a + b
  }
}
Run Code Online (Sandbox Code Playgroud)

或者您可以使用存在类型,因为您只关心它们是相同的:

object add extends Poly1 {
  implicit def caseTuple[A <: Foo { type T = _T } forSome { type _T }] =
    at[(A, A)] {
      case (a, b) => a + b
    }
}
Run Code Online (Sandbox Code Playgroud)

任一版本都将提供您想要的行为.