Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collect arguments to apply to curried functions in Java/Scala

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.

like image 737
Justin Kaeser Avatar asked Apr 21 '14 09:04

Justin Kaeser


People also ask

How do you pass parameters to a Scala function?

Scala - Functions with Named Arguments Named arguments allow you to pass arguments to a function in a different order. The syntax is simply that each argument is preceded by a parameter name and an equals sign. Try the following program, it is a simple example to show the functions with named arguments.

What is a curried function in Scala?

What are Curried Functions? Currying is a technique or a process for modifying a function in Scala. This function converts several parameters into a single argument function. Curried functions have multiple parameter lists.

What is function currying in Scala example?

Currying is the process of converting a function with multiple arguments into a sequence of functions that take one argument. Each function returns another function that consumes the following argument.

Why currying is useful in Scala?

Advantages of Currying Function in Scala One benefit is that Scala currying makes creating anonymous functions easier. Scala Currying also makes it easier to pass around a function as a first-class object. You can keep applying parameters when you find them.


2 Answers

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"

like image 79
NightRa Avatar answered Nov 15 '22 20:11

NightRa


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;
    }
};
like image 43
nosid Avatar answered Nov 15 '22 20:11

nosid