2014-04-21 4 views
9

Vorrei creare una classe in Java 8 che sia in grado di creare ricorsivamente un oggetto che abbia un metodo che utilizza un parametro di funzione in base ai parametri che ho aggiunto.Raccogli gli argomenti da applicare alle funzioni curry in Java/Scala

Ad esempio, vorrei essere in grado di fare questo:

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

Procedimento applica allora applicare i parametri raccolti per la funzione data.

Ritengo che ciò dovrebbe essere possibile senza riflessioni mentre si mantiene la sicurezza del tipo, ma non riesco a capire come. Una soluzione in Scala è anche benvenuta, se posso tradurla in Java 8. Se non è possibile, accetterò anche una risposta che spiega perché.

Quello che ho finora è essenzialmente questo:

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 
      ); 
    } 
} 

Come notato nei commenti di codice, i miei problemi stanno mantenendo i parametri della funzione nell'ordine delle chiamate di param(), e in realtà applicando i parametri .

+0

Sembra che tu abbia bisogno di Applicativo. – pedrofurla

risposta

2

Per una quantità illimitata di parametri, l'unica soluzione che ho potuto pensare è con liste eterogenei a Scala.

Probabilmente non è fattibile in Java in quanto è in corso un calcolo del livello di tipo con tipi dipendenti dal percorso.

Uso degli elenchi eterogenei e tipi dipendenti dal percorso:

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 
} 

Questo codice stampa:

23Hello 
65Hello 

La seconda, molto più limitato di fare questo, ma che potrebbe funzionare con Java, è quello di creare più classi per ogni lunghezza di funzione, che restituisce la classe di lunghezza della funzione successiva che racchiude il valore, fino ad una lunghezza massima - Vedere il generatore applicativo in Scalaz: "Scalaz Applicative Builder"

+0

piuttosto interessante, potrei andare in questo modo se decido di implementare qualcosa di simile in Scala :) –

1

Questo non risponde alla tua domanda. Tuttavia, forse aiuta qualcuno a trovare una soluzione, o a spiegare perché non è possibile in Java e/o Scala.

Può essere eseguito in C++, con un numero arbitrario di parametri e senza perdere la sicurezza del tipo. Il lato chiamata guarda come segue. Sfortunatamente, la sintassi lambda in C++ è piuttosto dettagliata.

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'; 
      }; 
     }; 
    }); 

In seguito è la definizione di foo e bar. L'implementazione è semplice. Tuttavia, dubito che sia possibile costruire qualcosa di simile in Java, perché il modo in cui i parametri di tipo funzionano in Java. Generics in Java può essere utilizzato solo per evitare il tipo e questo non è sufficiente per questo caso d'uso.

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

Mi dispiace solo potuto dare qualche porta a Scala:

Forse sarebbe utile dare un'occhiata a http://www.scala-lang.org/api/2.10.4/index.html#scala.Function $

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

praticamente assomiglia Function.uncurried

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

potrebbe essere implementato utilizzando un elenco per un accumulatore se non ti interessa la sicurezza del tipo. Se si desidera mantenere i tipi, è possibile utilizzare la lista HList di Shapeless https://github.com/milessabin/shapeless fornita con un pratico metodo a scelta a.

Attuazione param():

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

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

Esempio

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

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

HList è stato uno dei miei primi pensieri, ma non si è tradotto prontamente in Java :) Voglio mantenere i tipi se possibile, ma potrei accontentarmi di un trucco. –

+0

In attesa di una soluzione. Sembra tutto molto interessante. –