2013-06-12 4 views
4

мне было интересно, если вы можете написать общую функцию, которая принимает каррированную функцию, а затем переворачивают аргументы, например:Существует ли общий способ инвертирования порядка аргументов функции curried?

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.

+0

Я полагаю, вы имеете в виду с произвольным числом аргументов? – gzm0

+0

Точно, я имею в виду произвольное количество аргументов. –

ответ

5

TL; DR: Использовать this gist. Объяснение того же следующим образом:

Во-первых, определить класс типа (и случаи), чтобы частично применить функцию с последнего параметра:

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") 
+1

Умный и бесформенный. –

+1

@ som-snytt Но очень бесформенный: P – gzm0

+0

Запуск в Scala 2.9.3 дает ошибки: 'error: незаконный зависимый метод type inv: Инвертор [Rem]) = new Инвертор [A] {'. Есть ли способ обхода Scala 2.9? –

2

1) Извлечение информации о типе из вашей функции HList (от shapeless) Ts и типа аргумент Res.

Ts Для Int => String => Long => String является Int :: String :: Long :: HNil и Res является 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) Преобразование Curried функции 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 => String результата Int :: 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 
+0

Умный и бесформенный. Я смотрел на более короткое решение, мой первый взгляд, наконец, бесформенный, но нужно выяснить Реверс. –

+0

Возможно, вы захотите упомянуть бесформенную библиотеку где-нибудь;) (https://github.com/milessabin/shapeless) – gzm0

+0

@ gzm0: спасибо. Я забыл, что кто-то может не знать о бесформенной библиотеке. – senia

Смежные вопросы