收集参数以应用于Java / Scala中的curried函数

时间:2014-04-21 09:24:39

标签: java scala generics java-8 currying

我想在Java 8中创建一个类,它能够递归地创建一个对象,该对象的方法根据我添加的参数获取函数参数。

例如,我希望能够这样做:

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

然后,apply方法将收集的参数应用于给定的函数。

我认为在保持类型安全的同时,这应该是没有反思的,但我无法弄清楚如何。如果我可以将其转换为Java 8,那么Scala中的解决方案也是受欢迎的。如果不可能,我也会接受解释原因的答案。

到目前为止我所拥有的基本上是这样的:

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

如代码注释中所述,我的问题是按照param()调用的顺序保存函数参数,并实际应用参数。

3 个答案:

答案 0 :(得分:2)

对于无限量的参数,我能想到的唯一解决方案是使用Scala中的异构列表。

在Java中可能是不可行的,因为类型级计算正在继续使用路径依赖类型。

使用异类列表和路径依赖类型:

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
}

此代码打印:

23Hello
65Hello

第二种,更有限的方法,这可能适用于Java,是为每个函数长度创建多个类,它返回包含该值的下一个大小的函数长度类,直到某个最大长度 - 请参阅Scalaz中的Applicative Builder:"Scalaz Applicative Builder"

答案 1 :(得分:1)

这不能回答你的问题。但是,也许它可以帮助某人找到解决方案,或解释为什么在Java和/或Scala中不可能。

它可以在C ++中完成,具有任意数量的参数,并且不会丢失类型安全性。呼叫方面看起来如下。不幸的是,C ++中的lambda语法非常详细。

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

以下是foobar的定义。实施是直截了当的。但是,我怀疑在Java中构建类似的东西是可能的,因为类型参数在Java中的工作方式。 Java中的泛型只能用于避免类型转换,这对于这个用例来说还不够。

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

答案 2 :(得分:1)

抱歉,我可以在Scala中提供一些线索:

或许查看http://www.scala-lang.org/api/2.10.4/index.html#scala.Function $

会有所帮助
.apply((Integer a) -> (String b) -> (Object c) -> f(a,b,c))

非常像 Function.uncurried

param(23).param("some String").param(someObject)
如果您不关心类型安全性,可以使用累加器列表来实现

。如果你想保留类型,你可以使用无形https://github.com/milessabin/shapeless的HList,它带有一个方便的 tuppled 方法。

param()的实现:

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

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

实施例

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

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