7

私は、タイプを使用して、エンティティの単位をチェックする経済モデルの小さなライブラリに取り組んでいval apples = 2.0ますval apples = GoodsAmount[KG, Apples](2.0)。商品のバンドルを作成するために、シェイプレス ライブラリの HLists を使用しようとしています。これは問題なく動作しますが、場合によっては、私が好むほど汎用的なコードにすることはできません。たとえば、次の問題を参照してください。

何をシェイプレスに持ち上げたいかを説明する簡単なコードから始めます。Km、その他のマイルを表す 2 つのクラスを作成します。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 */
}

これは意図したとおりに機能します。ただし、「add」関数で型制約チェックを行う必要があります。これを 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)

type Constraint を caseTuple 関数に追加できた場合、最初のエラーを修正する必要がありますが、正直なところ、at 関数がどのように機能しているか、暗黙の証拠パラメーターをどこに追加できるかを理解していません。また、マッパーが暗黙の値を見つけるために何をしなければならないかもわかりません。

caseTuple 関数を次のように置き換えます。

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

正常に動作しますが、多くの冗長なコードを記述する必要があります (わかりました、このソリューションは、タプルを使用する現在のソリューションよりも優れています)。誰かがこの問題を解決する方法を教えてもらえますか?

ありがとう、クリンケ

4

1 に答える 1

7

ケースに型パラメーターを追加することで、型メンバーを一致させることができます。

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
    }
}

どちらのバージョンでも、必要な動作が提供されます。

于 2013-06-27T15:17:01.213 に答える