2014-04-21 6 views
9

Ich möchte eine Klasse in Java 8 erstellen, die in der Lage ist, ein Objekt rekursiv erstellen, die eine Funktion hat, die einen Funktionsparameter basierend auf den Parametern ich hinzugefügt.Collect Argumente für Curry-Funktionen in Java/Scala

Zum Beispiel würde Ich mag Lage sein, dies zu tun:

new X().param(23).param("some String").param(someObject) 
    .apply((Integer a) -> (String b) -> (Object c) -> f(a,b,c)) 

Verfahren anwenden würde dann die gesammelten Parameter an die gegebene Funktion anwenden.

Ich denke, dass dies möglich sein sollte, ohne zu reflektieren, während die Art-Sicherheit beibehalten wird, aber ich kann nicht recht herausfinden, wie. Eine Lösung in Scala ist ebenfalls willkommen, wenn ich sie in Java 8 übersetzen kann. Wenn es nicht möglich ist, werde ich auch eine Antwort akzeptieren, die erklärt, warum.

Was ich habe, so weit ist im Wesentlichen diese:

class ParamCmd<A,X> { 

    final A param; 

    public ParamCmd(A param) { 
     this.param = param; 
    } 

    public<B> ParamCmd<B, Function<A,X>> param(B b) { 
     return new ParamCmd<>(b); 
    } 

    public void apply(Function<A,X> f) { 
     // this part is unclear to me 
    } 

    public static void main(String[] args) { 
     new ParamCmd<Integer,String>(0).param("oops").param(new Object()) 
      // the constructed function parameters are reversed relative to declaration 
      .apply((Object c) -> (String b) -> (Integer a) -> 
       "args were " + a + " " + b + " " + c 
      ); 
    } 
} 

Wie in den Code-Kommentaren erwähnt, meine Probleme halten die Funktionsparameter in der Reihenfolge der Anrufe von param() und die Anwendung tatsächlich die Parameter .

+0

Es scheint, Sie brauchen Applicative. – pedrofurla

Antwort

2

für eine unbegrenzte Anzahl von Parametern, der die einzige Lösung, die ich denken konnte, ist mit Heterogene Listen in Scala.

Es ist wahrscheinlich in Java nicht machbar, da bei Pfad-abhängigen Typen eine Typ-Level-Berechnung stattfindet.

Mit Heterogene Listen und Wegabhängige Typen:

import scala.language.higherKinds 

object Main extends App { 
    val builder1 = HCons(23, HCons("Hello", HNil)) 
    val builder2 = HCons(42L, builder1) 

    val res1:String = builder1.apply(i => s => i + s) 
    val res2:String = builder2.apply(l => i => s => (i+l) + s) 
    println(res1) // 23Hello 
    println(res2) // 65Hello 
} 

sealed trait HList { 
    type F[Res] 
    def apply[Res]: F[Res] => Res 
} 
case class HCons[Head, HTail <: HList](head: Head, tail: HTail) extends HList { 
    type F[Res] = Head => (tail.type)#F[Res] 
    def apply[Res]: F[Res] => Res = f => tail.apply(f(head)) 
} 
case object HNil extends HList { 
    type F[Res] = Res 
    def apply[Res]: F[Res] => Res = identity 
} 

Dieser Code druckt:

23Hello 
65Hello 

Die zweite, begrenzte Möglichkeit, dies zu tun, aber die mit Java arbeiten könnte, ist zu Erstellen Sie mehrere Klassen für jede Funktionslänge, die die nächstgrößere Funktionslängenklasse zurückgibt und den Wert bis zu einer maximalen Länge einhüllt - siehe Applicative Builder in Scalaz: "Scalaz Applicative Builder"

+0

ziemlich cool, ich könnte diesen Weg gehen, wenn ich beschließe, etwas Ähnliches in Scala zu implementieren :) –

1

Dies beantwortet Ihre Frage nicht. Aber vielleicht hilft es jemandem, eine Lösung zu finden oder zu erklären, warum es in Java und/oder Scala nicht möglich ist.

Es kann in C++ mit einer beliebigen Anzahl von Parametern und ohne Verlust der Typ-Sicherheit durchgeführt werden. Die Anrufseite sieht folgendermaßen aus. Leider ist die Lambda-Syntax in C++ ziemlich ausführlich.

bar{}.param(23).param("some String").param(4.2).apply(
    [](int i) { 
     return [=](std::string s) { 
      return [=](double d) { 
       std::cout << i << ' ' << s << ' ' << d << '\n'; 
      }; 
     }; 
    }); 

Es folgt die Definition von foo und bar. Die Implementierung ist unkompliziert. Ich bezweifle jedoch, dass es möglich ist, in Java so etwas zu erstellen, weil die Typ-Typ-Parameter in Java funktionieren. Generics in Java können nur verwendet werden, um Typ Umwandlungen zu vermeiden, und das ist nicht genug für diesen Anwendungsfall.

template <typename Param, typename Tail> 
struct foo { 
    Param _param; 
    Tail _tail; 
    template <typename P> 
    auto param(P p) { 
     return foo<P, foo>{p, *this}; 
    } 
    template <typename Function> 
    auto apply(Function function) { 
     return _tail.apply(function)(_param); 
    } 
}; 

struct bar { 
    template <typename P> 
    auto param(P p) { 
     return foo<P, bar>{p, *this}; 
    } 
    template <typename Function> 
    auto apply(Function function) { 
     return function; 
    } 
}; 
1

Leider konnte ich nur einige führt in Scala geben:

Vielleicht wäre es einen Blick auf http://www.scala-lang.org/api/2.10.4/index.html#scala.Function $

.apply((Integer a) -> (String b) -> (Object c) -> f(a,b,c)) 

ziemlich sieht aus wie Function.uncurried

haben helfen
param(23).param("some String").param(someObject) 

könnte mithilfe einer Liste für eine implementiert werden Akkumulator, wenn Sie sich nicht für Typ Sicherheit interessieren. Wenn Sie die Typen behalten möchten, können Sie die HList aus Shapeless https://github.com/milessabin/shapeless verwenden, die mit einer handlichen tuppled Methode kommt.

Implementierung von param():

import shapeless._ 
import HList._ 
import syntax.std.traversable._ 

class Method(val l : HList = HNil) { 
    def param(p: Any) = new Method(p :: l) 
} 

Beispiel

scala> val m = new Method().param(1).param("test") 
m: Method = [email protected] 

scala> m.l 
res8: shapeless.HList = test :: 1 :: HNil 
+0

HList war einer meiner ersten Gedanken, aber formlos lässt sich nicht ohne weiteres in Java übersetzen :) Ich möchte die Typen wenn möglich behalten, aber ich könnte mich mit einem Hack begnügen. –

+0

Ich freue mich auf Ihre Lösung. Das sieht alles sehr interessant aus. –