9

Cブール値だけでなく、さまざまな数値型の値を格納するクラスを実装したいと思います。さらに、このクラスのインスタンスを型間で操作し、必要Int --> Doubleに応じて変換できるようにしたいと考えてBoolean -> Intいます。Boolean + BooleanInt + BooleanBoolean + IntInt + DoubleDouble + DoubleIntDouble

これまでのところ、私はこれを思いつきました:

abstract class SemiGroup[A] { def add(x:A, y:A):A }

class C[A] (val n:A) (implicit val s:SemiGroup[A]) {
  def +[T <% A](that:C[T]) = s.add(this.n, that.n)
}

object Test extends Application {
  implicit object IntSemiGroup extends SemiGroup[Int] { 
    def add(x: Int, y: Int):Int = x + y 
  }

  implicit object DoubleSemiGroup extends SemiGroup[Double] { 
    def add(x: Double, y: Double):Double = x + y 
  }

  implicit object BooleanSemiGroup extends SemiGroup[Boolean] { 
    def add(x: Boolean, y: Boolean):Boolean = true;
  }

  implicit def bool2int(b:Boolean):Int = if(b) 1 else 0

  val n = new C[Int](10)
  val d = new C[Double](10.5)
  val b = new C[Boolean](true)

  println(d + n)    // [1]
  println(n + n)    // [2]
  println(n + b)    // [3]
  // println(n + d)    [4] XXX - no implicit conversion of Double to Int exists
  // println(b + n)    [5] XXX - no implicit conversion of Int to Boolean exists
}

これは一部のケース (1、2、3) では機能しますが、(4、5) では機能しません。その理由は、下位から上位への型の暗黙的な拡張があるためですが、その逆はありません。方法としては、

def +[T <% A](that:C[T]) = s.add(this.n, that.n)

どういうわけか、次のようなパートナーメソッドが必要です。

def +[T, A <% T](that:C[T]):T = that.s.add(this.n, that.n)

しかし、これは 2 つの理由でコンパイルされません。1 つ目は、コンパイラがthis.n型に変換できないことT(ビュー バウンドを指定A <% Tしても)、2 つ目は、変換できたとしても、this.n型消去後に 2 つの+メソッドがあいまいになることです。

すみません、これはとても長いです。どんな助けでも大歓迎です!それ以外の場合は、すべてのタイプ間のすべての操作を明示的に書き出す必要があるようです。そして、私が余分なタイプを追加しなければならなかった場合、それは毛むくじゃらになります(Complexメニューの次の...)。

たぶん、誰かがこれらすべてを完全に達成する別の方法を持っているのでしょうか? 私が見落としている単純なものがあるように感じます。

前もって感謝します!

4

2 に答える 2

6

では、ダニエル!

ブール値を無視するようにソリューションを制限しAnyVals、のインスタンスを持つ弱い上界と下界を持つものでのみ機能しますNumeric。これらの制限は恣意的であり、それらを削除して、タイプ間の独自の弱い適合関係をエンコードすることができます-の実装a2ba2cいくつかの変換を実行することができます。

暗黙のパラメーターが継承(型(Derived => Base)または弱い適合性の暗黙のパラメーターを渡す)をどのようにシミュレートできるかを検討するのは興味深いことです。特に型推論器が役立つ場合、これらは非常に強力です。

まず、すべてのタイプのペアの弱い最小上界を表すタイプクラスが必要であり、A関心Bがあります。

sealed trait WeakConformance[A <: AnyVal, B <: AnyVal, C] {
  implicit def aToC(a: A): C

  implicit def bToC(b: B): C
}

object WeakConformance {
  implicit def SameSame[T <: AnyVal]: WeakConformance[T, T, T] = new WeakConformance[T, T, T] {
    implicit def aToC(a: T): T = a

    implicit def bToC(b: T): T = b
  }

  implicit def IntDouble: WeakConformance[Int, Double, Double] = new WeakConformance[Int, Double, Double] {
    implicit def aToC(a: Int) = a

    implicit def bToC(b: Double) = b
  }

  implicit def DoubleInt: WeakConformance[Double, Int, Double] = new WeakConformance[Double, Int, Double] {
    implicit def aToC(a: Double) = a

    implicit def bToC(b: Int) = b
  }

  // More instances go here!


  def unify[A <: AnyVal, B <: AnyVal, C](a: A, b: B)(implicit ev: WeakConformance[A, B, C]): (C, C) = {
    import ev._
    (a: C, b: C)
  }
}

このメソッドunifyは型を返しますC。これは、暗黙の引数として提供する暗黙の値の可用性に基づいて型推論によって計算されますev

これをラッパークラスCに次のようにプラグインできNumeric[WeakLub]ます。また、値を追加できるようにする必要があります。

case class C[A <: AnyVal](val value:A) {
  import WeakConformance.unify
  def +[B <: AnyVal, WeakLub <: AnyVal](that:C[B])(implicit wc: WeakConformance[A, B, WeakLub], num: Numeric[WeakLub]): C[WeakLub] = { 
    val w = unify(value, that.value) match { case (x, y) => num.plus(x, y)}; 
    new C[WeakLub](w)
  }
}

そして最後に、すべてをまとめます。

object Test extends Application {
  val n = new C[Int](10)
  val d = new C[Double](10.5)

  // The type ascriptions aren't necessary, they are just here to 
  // prove the static type is the Weak LUB of the two sides.
  println(d + n: C[Double]) // C(20.5)
  println(n + n: C[Int])    // C(20)
  println(n + d: C[Double]) // C(20.5)
}

Test
于 2010-06-22T05:52:40.550 に答える
3

There's a way to do that, but I'll leave it to retronym to explain it, since he wrote this solution. :-)

于 2010-06-22T00:22:17.753 に答える