5

次のように、カリー化された関数を受け取り、引数を反転する汎用関数を記述できるかどうか疑問に思っていました。

def foo(a: String)(b: Boolean)(c: Int): String
val bar = invert(foo _)
foo("baz")(false)(12) must be equalTo(bar(12)(false)("baz"))

以下は、対処したい特定のケースに暗黙的なインバーターを追加する限り機能します。しかし、私はより一般的なケース、つまり、任意の数のカリー化された引数を扱うケースにもっと興味があります。

trait Inverter[V, W] {
  def invert(v: V): W
}

implicit def function2Inverter[X, Y, Z] = 
  new Inverter[(X, Y) => Z, (Y, X) => Z] {
    def invert(v: (X, Y) => Z) = {
      def inverted(y: Y, x: X) = v(x, y)
      inverted _
    }
  }

implicit def curried2Inverter[X, Y, Z] = 
  new Inverter[X => Y => Z, Y => X => Z] {
    def invert(v: (X) => (Y) => Z) = {
      def inverted(y: Y)(x: X) = v(x)(y)
      inverted _
    }
  }

def invert[V, W](v: V)(implicit inverter: Inverter[V, W]): W = 
  inverter.invert(v)

ああ、Scala 2.9 でも動作するソリューションが欲しいです。

4

2 に答える 2

5

TL;DR:この要点を使用してください。同じことの説明は次のとおりです。

最初に、型クラス (およびケース) を定義して、最後のパラメーターで関数を部分的に適用します。

trait PopLast[A, Last, Rem] {
  def pop(f: A, v: Last): Rem
}

trait LowPrioPopLast {
  implicit def popEnd[A,B] = new PopLast[A => B, A, B] {
    def pop(f: A => B, v: A) = f(v)
  }  
}

object PopLast extends LowPrioPopLast {
  implicit def popOne[A, B, C, Last, IRem](
    implicit iPop: PopLast[B => C, Last, IRem]) = 
      new PopLast[A => B => C, Last, A => IRem] {
        def pop(f: A => B => C, v: Last) = { a: A => iPop.pop(f(a), v) }
      }
}

次に、インバーター型クラスを作成します。最後のパラメーターの部分適用を再帰的に行い、結果を反転します。

trait Inverter[A] {
  type Out
  def invert(f: A): Out 
}

trait LowPrioInverter {
  implicit def invertEnd[A,B] = new Inverter[A => B] {
    type Out = A => B
    def invert(f: A => B) = f
  }
}

object Inverter extends LowPrioInverter {
  implicit def invertStep[A, Last, Rem](implicit pop: PopLast[A, Last, Rem],
    inv: Inverter[Rem]) = new Inverter[A] {
      type Out = Last => inv.Out
      def invert(f: A) = { a: Last => inv.invert(pop.pop(f, a)) }
  }
}

最後に、関数にカプセル化します。

def invert[A](f: A)(implicit inv: Inverter[A]) = inv.invert(f)

そして、そこに行きます:

def foo(a: String)(b: Boolean)(c: Int): String = "bar"
val bar = invert(foo _)
// bar: Int => Boolean => String => String

bar(1)(true)("foo")
于 2013-06-12T17:35:03.577 に答える