【问题标题】:Shapeless: Checking Type Constraints of Polymorphic functionsShapeless:检查多态函数的类型约束
【发布时间】:2013-06-27 14:38:34
【问题描述】:

我正在开发一个小型经济模型库,用于检查实体的单位,使用类型,例如而不是val apples = 2.0,我们写val apples = GoodsAmount[KG, Apples](2.0)。为了创建捆绑商品,我尝试使用 shapeless 库中的 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 */
}

这按预期工作。但是有必要对“添加”功能进行类型约束检查。我将其扩展到 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)
}

但这会产生以下错误消息(使用 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)

第一个错误应该修复,如果我可以向 caseTuple 函数添加类型约束,但老实说,我不明白 at 函数是如何工作的,以及我可以在哪里添加隐式证据参数。而且我也不知道,我必须做什么,这样 Mapper 才能找到他的隐含价值。

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

implicit def caseTuple = at[(Km,Km)] { case (a, b) => a + b }

工作正常,但需要编写大量冗余代码(好吧,这个解决方案会比我们当前使用元组的解决方案更好)。有人能告诉我如何解决这个问题吗?

谢谢, 克林克

【问题讨论】:

  • 您可以尝试像这样定义您的Footrait Foo[T &lt;: Foo] { v: Double; +(t T): T =...}class Km(val v: Double) extends Foo[Km]implicit def add[T] = at[(Foo[T], Foo[T])]

标签: scala shapeless hlist


【解决方案1】:

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

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

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

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

任何一个版本都会提供您想要的行为。

【讨论】:

  • 谢谢,这在我更复杂的情况下也可以正常工作 ;-) 但是我仍然有 Mapper 缺少隐式值的问题。我会尝试自己解决它,但也许你也可以在这里帮助我?
  • 好的,在简化版中找到了解决方案,为 A 添加上下文绑定会有所帮助。所以我现在有implicit def caseTuple[_t, A &lt;: Foo { type T = _T } &lt;% Foo { type T = _T }] = ... 这一次解决方案并没有那么容易地转化为我的完整版本,但希望我也能解决新问题。
  • @Klinke:我不确定我是否理解这个问题——如果我将我的add 复制并粘贴到你的ExampleShapeless 中,一切似乎都按预期工作。
  • 我刚刚使用实际的无形源进行了测试,但您的添加对象仍然出现此编译器错误:could not find implicit value for parameter mapper: shapeless.Mapper[ExampleShapeless.add.type,shapeless.::[(Km, Km),shapeless.::[(Km, Km),shapeless.HNil]]]。将(implicit ev: A =&gt; Foo { type T = _T }) 添加到 caseTuple 函数会有所帮助,在我的库中也是如此。
猜你喜欢
  • 2015-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-12
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
  • 2012-01-06
相关资源
最近更新 更多