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 回答
2

1)从你的函数中提取类型信息到HList(从shapelessTs和类型参数Res

对于Int => String => Long => String TsisInt :: String :: Long :: HNilResis String

import shapeless.{HList, HNil, ::}

trait TypeExtractorAux[A, R, Ts <: HList, Res]

trait LowPriorityTypeExtractorAux{
  implicit def apply1[A, R]: TypeExtractorAux[A, R, A :: HNil, R] = new TypeExtractorAux[A, R, A :: HNil, R]{}
}

object TypeExtractorAux extends LowPriorityTypeExtractorAux{
  implicit def applyN[A1, A2, R, TsNext <: HList, Res](implicit te: TypeExtractorAux[A2, R, TsNext, Res]): TypeExtractorAux[A1, A2 => R, A1 :: TsNext, Res] =
    new TypeExtractorAux[A1, A2 => R, A1 :: TsNext, Res]{}
}

2)将咖喱函数转换为Ts => Res

trait FunctionConverterAux[Ts <: HList, Res, A, R]{
  def apply(f: A => R): Ts => Res
}

object FunctionConverterAux{
  implicit def apply1[L, Res]: FunctionConverterAux[L :: HNil, Res, L, Res] =
    new FunctionConverterAux[L :: HNil, Res, L, Res]{
      def apply(f: L => Res): L :: HNil => Res = hl => f(hl.head)
    }

  implicit def applyN[L1, L2, Rt <: HList, Res, R](implicit fc: FunctionConverterAux[L2 :: Rt, Res, L2, R]): FunctionConverterAux[L1 :: L2 :: Rt, Res, L1, L2 => R] =
    new FunctionConverterAux[L1 :: L2 :: Rt, Res, L1, L2 => R]{
      def apply(f: L1 => L2 => R): L1 :: L2 :: Rt => Res = hl => fc(f(hl.head))(hl.tail)
    }
}

结果Int => String => Long => StringInt :: String :: Long :: HNil => String

3)反转Ts使用shapeless.HList.ReverseAux并收集所有参数HList。你会得到Ts. 然后将您的Ts => Res函数应用于HList参数:

trait ArgumentsGetterAux[Prev <: HList, Rest <: HList, NR, Ts, Res]{
  def apply(p: Prev, f: Ts => Res): NR
}

object ArgumentsGetterAux{
  implicit def applyHNil[Prev <: HList, L, Res]: ArgumentsGetterAux[Prev, L :: HNil, L => Res, L :: Prev, Res] =
    new ArgumentsGetterAux[Prev, L :: HNil, L => Res, L :: Prev, Res]{
      def apply(p: Prev, f: L :: Prev => Res): L => Res = l => f(l :: p)
    }

  implicit def applyHList[Prev <: HList, L1, R <: HList, NR, Ts, Res](implicit aga: ArgumentsGetterAux[L1 :: Prev, R, NR, Ts, Res]): ArgumentsGetterAux[Prev, L1 :: R, L1 => NR, Ts, Res] =
    new ArgumentsGetterAux[Prev, L1 :: R, L1 => NR, Ts, Res]{
      def apply(p: Prev, f: Ts => Res): L1 => NR = l => aga(l :: p, f)
    }
}

invert方法:

import shapeless.HList.ReverseAux

def invert[A, R, Ts <: HList, Res, RevTs <: HList, NR](f: A => R)(
    implicit te: TypeExtractorAux[A, R, Ts, Res],
             r: ReverseAux[Ts, RevTs],
             ag: ArgumentsGetterAux[HNil, RevTs, NR, Ts, Res],
             fc: FunctionConverterAux[Ts, Res, A, R]
  ): NR = ag(HNil, fc(f))

用法:

scala> invert((i: Int) => (s: String) => (l: Long) => s"i: $i; s: $s; l: $l")
res0: Long => (String => (Int => String)) = <function1>

scala> res0(666L)("str")(1)
res1: String = i: 1; s: str; l: 666
于 2013-06-12T18:20:23.750 回答