I would like to create a class in Java 8 which is able to recursively create an object which has a method that takes a function parameter based on the parameters I added.

For example, I would like to be able to do this:

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

The apply method would then apply the collected parameters to the given function.

I feel this should be possible without reflection while maintaing type-safety, but I can't quite figure out how. A solution in Scala is also welcome, if I can translate it to Java 8. If it's not possible, I'll also accept an answer that explains why.

What I have so far is essentially this:

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

As noted in the code comments, my problems are keeping the function parameters in the order of the calls of param(), and actually applying the parameters.

有帮助吗?

解决方案

For an unlimited amount of parameters, the only solution I could think of is with Heterogeneous Lists in Scala.

It is probably isn't feasible in Java as there is type level computation going on with path-dependant types.

Using Heterogeneous Lists and Path-Dependant types:

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
}

This code prints:

23Hello
65Hello

The second, more limited way of doing this, but which might work with Java, is to create multiple classes for each function length, which returns the next sized function length class wrapping the value, up to some maximal length - See the Applicative Builder in Scalaz: "Scalaz Applicative Builder"

其他提示

This doesn't answer your question. However, maybe it helps someone to find a solution, or to explain why it isn't possible in Java and/or Scala.

It can be done in C++, with an arbitrary number of parameters, and without losing type-safety. The call-side look as follows. Unfortunately, the lambda syntax in C++ is quite verbose.

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

Following is the definition of foo and bar. The implementation is straight-forward. However, I doubt that it is possible to build something like this in Java, because the way type parameters work in Java. Generics in Java can only be used to avoid type casts, and that's not enough for this use case.

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

Sorry I just could give some leads in Scala:

Perhaps it would help to have a look at http://www.scala-lang.org/api/2.10.4/index.html#scala.Function$

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

pretty much looks like Function.uncurried

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

could be implemented using a list for an accumulator if you don't care for Type safety. If you want to keep the Types you could use the HList out of Shapeless https://github.com/milessabin/shapeless which comes with a handy tuppled method.

Implementation of param():

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

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

Example

scala> val m = new Method().param(1).param("test")
m: Method = Method@1130ad00

scala> m.l
res8: shapeless.HList = test :: 1 :: HNil
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top