2014-04-21 9 views
9

Chciałbym utworzyć klasę w języku Java 8, która jest w stanie rekurencyjnie utworzyć obiekt, który ma metodę, która pobiera parametr funkcji na podstawie parametrów dodanych przeze mnie.Zbierz argumenty do zastosowania do funkcji curry w Java/Scala

Na przykład chciałbym być w stanie to zrobić:

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

Sposób zastosowania byłoby następnie zastosować zebrane parametry danej funkcji.

Uważam, że powinno to być możliwe bez refleksji przy jednoczesnym zachowaniu bezpieczeństwa typu, ale nie jestem w stanie zrozumieć, w jaki sposób. Rozwiązanie w Scali jest również mile widziane, jeśli mogę przetłumaczyć je na język Java 8. Jeśli nie jest to możliwe, przyjmuję odpowiedź, która wyjaśnia, dlaczego.

Co mam tak daleko jest zasadniczo to:

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

Jak zauważył w komentarzach kodu, moje problemy są zachowując parametry funkcji w kolejności wywołań param() i faktycznie zastosowaniu parametrów .

+0

Wygląda na to, że potrzebujesz Aplikacji. – pedrofurla

Odpowiedz

2

dla nieograniczonej ilości parametrów, jedynym rozwiązaniem może myślę jest niejednorodnym Listy Scala.

Prawdopodobnie nie jest to możliwe w Javie, ponieważ istnieją obliczenia typu na poziomie zależnym od typu ścieżki.

Korzystanie heterogenicznych listach i path-Dependent typy:

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 
} 

Ten kod drukuje:

23Hello 
65Hello 

drugi, bardziej ograniczony sposób to zrobić, ale który może pracować z Java, ma utwórz wiele klas dla każdej długości funkcji, która zwróci wartość o następnej klasie długości funkcji, zawijając wartość, aż do pewnej maksymalnej długości - Zobacz Konstruktor apliakcji w Scalaz: "Scalaz Applicative Builder"

+0

całkiem fajnie, mógłbym pójść tą drogą, gdybym zdecydował się zaimplementować coś podobnego w Scali :) –

1

To nie odpowiada na twoje pytanie. Może jednak pomaga komuś znaleźć rozwiązanie lub wyjaśnić, dlaczego nie jest to możliwe w Javie i/lub Scali.

Można to zrobić w C++, z dowolną liczbą parametrów i bez utraty bezpieczeństwa typu. Wygląd strony wywoławczej wygląda następująco. Niestety, składnia lambda w C++ jest dość obszerna.

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

Poniżej definicja foo i bar. Implementacja jest prosta. Wątpię jednak, aby można było zbudować coś takiego w Javie, ponieważ sposób działania parametrów typów w Javie. Generics w Javie można używać tylko w celu uniknięcia rzutów typu , a to nie wystarczy w tym przypadku użycia.

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

Niestety właśnie może dać jakieś przewody w Scala:

Może to pomóc spojrzeć na http://www.scala-lang.org/api/2.10.4/index.html#scala.Function $

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

dość dużo wygląda Function.uncurried

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

może zostać zaimplementowany przy użyciu listy akumulator, jeśli nie zależy Ci na bezpieczeństwie typu. Jeśli chcesz zachować typy, możesz użyć HList z Shapeless https://github.com/milessabin/shapeless, który jest dostarczany z przydatną metodą z tuppled.

Wykonanie param():

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

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

Przykład

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

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

HList był jednym z moich pierwszych myśli, ale bezkształtny niełatwo przetłumaczyć na język Java :) Chcę zachować typy, jeśli to możliwe, ale mogę zadowalać się włamaniem. –

+0

Czekamy na Twoje rozwiązanie. To wszystko wygląda całkiem interesująco. –