9

This is a question that I wondered about since the lambdas had been introduced in Java, and inspired by a related question, I thought that I might bring it up here, to see whether there are any ideas.

(Side notes: There is a similar question for C#, but I did not find one for Java. The questions for Java about "storing a lambda in a variable" always referred to cases where the type of the variable was fixed - this is exactly what I'm trying to circumvent)


Lambda expressions receive the type that they need, via target type inference. This is all handled by the compiler. For example, the functions

static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }

can both be called with the same lambda expression:

useF(x -> true);
useP(x -> true);

The expression will once manifest itself as a class implementing the Function<Integer,Boolean> interface, and once as a class implementing the Predicate<Integer> interface.

But unfortunately, there is no way of storing the lambda expression with a type that is applicable to both functions, like in

GenericLambdaTypelambda = x -> true;

This "generic lambda type" would have to encode the type of the method that can be implemented by the given lambda expression. So in this case, it would be

(Ljava.lang.Integer)Ljava.lang.Booleanlambda = x -> true;

(based on the standard type signatures, for illustration). (This is not completely unreasonable: The C++ lambda expressions are basically doing exactly that...)


So is there any way to prevent a lambda expression being resolved to one particular type?

Particularly, is there any trick or workaround that allows the useF and useP methods sketched above to be called with the same object, as in

useF(theObject);
useP(theObject);

This is unlikely, so I assume the answer will plainly be: "No", but: Could there be any way to write a generic, magic adaption method like

useF(convertToRequiredTargetType(theObject));
useP(convertToRequiredTargetType(theObject));

?


Note that this question is more out of curiosity. So I'm literally looking for any way to achieve this (except for custom precompilers or bytecode manipulation).

There seem to be no simple workarounds. A naive attempt to defer the type inference, by wrapping the expression into a generic helper method, as in

static <T> T provide()
{
    return x -> true;
}

of course fails, stating that "The target type of this expression must be a functional interface" (the type can simply not be inferred here). But I also considered other options, like MethodHandles, brutal unchecked casts or nasty reflection hacks. Everything seems to be lost immediately after the compilation, where the lambda is hidden in an anonymous object of an anonymous class, whose only method is called via InvokeVirtual...

Community
  • 1
  • 1
Marco13
  • 53,703
  • 9
  • 80
  • 159
  • A `CallSite`? Basically, that is what a lambda is – fge Mar 31 '16 at 15:00
  • Are you looking for a way to convert from an applicable functional interface to the equivalent, desired functional interface, e.g. `Function` to `Predicate`? Is this so you can have one lambda stored as a variable of one functional interface type but you can use it as another functional interface type? – rgettman Mar 31 '16 at 15:12
  • @fge I'm not sure - from looking over the docs, I don't see how this could be applied here. @rgettman Basically yes. I know that one could wrap it into another lambda, like `useP(x -> f.apply(x));`, but this does not work generically. – Marco13 Mar 31 '16 at 15:55
  • @JornVernee Quoting from the question: *"So I'm literally looking for any way to achieve this **(except for custom precompilers or bytecode manipulation)**."* – Marco13 Jul 21 '16 at 00:25
  • @Marco13 Sorry, I got lost in thought after about an hour looking at this :/ – Jorn Vernee Jul 21 '16 at 00:25

2 Answers2

3

I don't see any way of allowing a lambda expression that resolves to one particular functional interface type to be interpreted directly as an equivalent functional interface type. There is no superinterface or "generic lambda type" that both functional interfaces extend or could extend, i.e. that enforces that it takes exactly one parameter of exactly one specific type and returns a specific type.

But you can write a utility class with methods to convert from one type of functional interface to another.

This utility class converts predicates to functions that return booleans and vice versa. It includes identity conversions so that you don't have to worry about whether to call the conversion method.

public class Convert
{
    static <T> Predicate<T> toPred(Function<? super T, Boolean> func)
    {
        return func::apply;
    }

    static <T> Predicate<T> toPred(Predicate<? super T> pred)
    {
        return pred::test;
    }

    static <T> Function<T, Boolean> toFunc(Predicate<? super T> pred)
    {
        return pred::test;
    }

    static <T> Function<T, Boolean> toFunc(Function<? super T, Boolean> func)
    {
        return func::apply;
    }
}

The input functions and predicates are consumers of T, so PECS dictates ? super. You could also add other overloads that could take BooleanSuppliers, Supplier<Boolean>, or any other functional interface type that returns a boolean or Boolean.

This test code compiles. It allows you to pass a variable of a functional interface type and convert it to the desired functional interface type. If you already have the exact functional interface type, you don't have to call the conversion method, but you can if you want.

public class Main
{
    public static void main(String[] args)
    {
        Function<Integer, Boolean> func = x -> true;
        useF(func);
        useF(Convert.toFunc(func));
        useP(Convert.toPred(func));

        Predicate<Integer> pred = x -> true;
        useP(pred);
        useP(Convert.toPred(pred));
        useF(Convert.toFunc(pred));
    }

    static void useF(Function<Integer, Boolean> f) {
        System.out.println("function: " + f.apply(1));
    }
    static void useP(Predicate<Integer> p) {
        System.out.println("predicate: " + p.test(1));
    }
}

The output is:

function: true
function: true
predicate: true
predicate: true
predicate: true
function: true
rgettman
  • 176,041
  • 30
  • 275
  • 357
  • Yes, this is basically what I referred to in [this comment](http://stackoverflow.com/questions/36336848/is-there-any-way-to-circumvent-the-typedness-of-lambda-expressions/36339527#comment60298323_36336848). It's possible to convert lambdas by re-wrapping them, but there is no magical `Convert.toTargetType` method. The problem here is that `n*n` conversion methods are necessary for `n` types, although each type already satisfies the *requirements* for being applicable as the other... – Marco13 Mar 31 '16 at 22:37
3

Suppose in a shiny future (say Java 10 or 11) we have true function types which allow to specify a function without forcing it to be of a particular conventional Java type (and being some kind of value rather than an object, etc.). Then we still have the issue that the existing methods

static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }

expect a Java object implementing a conventional Java interface and behaving like Java objects do, i.e. not suddenly changing the result of theObject instanceof Function or theObject instanceof Predicate. This implies that it will not be the generic function that suddenly starts implementing the required interface when being passed to either of these methods but rather that some kind of capture conversion applies, producing an object implementing the required target interface, much like today, when you pass a lambda expression to either of these methods or when you convert a Predicate to a Function using p::test (or vice versa using f::apply).

So what won’t happen is that you are passing the same object to both methods. You only have an implicit conversion, which will determined at compile-time and likely made explicit in byte code just as with today’s lambda expressions.


A generic method like convertToRequiredTargetType can’t work because it has no knowledge about the target type. The only solutions to make such a thing work are the ones you have precluded, precompilers and byte code manipulation. You could create a method accepting an additional parameter, a Class object describing the require interface, which delegates to the LambdaMetaFactory but that method would have to redo everything the compiler does, determining the functional signature, the name of the method to implement, etc.

For no benefit, as invoking that utility method like convertToRequiredTargetType(theObject) (or actually convertToRequiredTargetType(theObject, Function.class)) is in no way simpler than, e.g. theObject::test). Your desire to create such a method caused your weird statement “Everything seems to be lost immediately after the compilation, where the lambda is hidden in an anonymous object of an anonymous class” when actually, you have an object implementing a functional interface with a known signature and therefore can be converted as simple as function::methodName (where the IDE can complete the method name for you, if you have forgotten)…

Holger
  • 285,553
  • 42
  • 434
  • 765
  • Of course, there are some technical issues. From a high-level, "language-centered" point of view (in contrast to a VM-centered one), one might wonder why it should not be possible for `(x instanceof Function)` and `Predicate)` to return `true` for *one and the same object*. So in this regard, I'm not sure whether such an implicit conversion would *really* be necessary. But sure, one step further, one could ask what `x.getClass().getMethods()` should return then.... (tbc) – Marco13 Apr 06 '16 at 19:28
  • The question about the "magic conversion method" was rather out of curiosity, whether this is *technically* possible. For example, regarding the `provide` method that I sketched, one could (naively!!!) ask whether it's not possible to "pull the type inference into the method" - basically, to determine that the `T` that is required *where this method is called* is indeed a functional interface. (I can't give a spot-on reason for this, except for "type inference is frigginly complicated, and there are likely cases where this would not be possible"). – Marco13 Apr 06 '16 at 19:32
  • Of course, there is no problem in having an object implementing both interfaces and you can create such thing via lambda expressions even today, just think `default` methods and intersection types. That choice is still made at compile-time, but could be deferred to creation time. But the point is, you can’t defer it to the time when a particular type is required. Keep in mind that Java allows dynamic loading of classes. There could be a compatible functional interface which hasn’t loaded yet, when the lambda object has been created. – Holger Apr 07 '16 at 09:18
  • As said, the result of `x instanceof Function` must not suddenly change for the same `x` due to the appliance of the magic conversion feature. I never said that `x instanceof Function` and `x instanceof Predicate` can’t be both `true` at the same time. That’s possible and rather easy to achieve. – Holger Apr 07 '16 at 09:22
  • I have no idea what your magic `convertToRequiredTargetType` is aiming at. There is a technical solution capable of doing what you describe, except that it doesn’t work for method invocation, but the much simpler method reference expression. Why should anyone change the Java language just to allow to use an existing feature in a more complicated manner? – Holger Apr 07 '16 at 09:25
  • It's OK. The implicit answer is "It is not possible", and there are some ways to "emulate" a similar behavior (although this may have its own issues ... I'm not sure whether `void f(Function f) { p(f); } void p(Predicate p) { f(p); }` would be convincing or whether it would provoke a statement that this (particular example in the given form) does not make sense. – Marco13 Apr 07 '16 at 16:27
  • You can already write ` void f(Function f) { p(f::apply); } void p(Predicate p) { f(p::test); }` today, but of course, if you do it exactly this way, you’ll likely get a `StackOverflowError`… Note that this feature doesn’t even require the function to be implemented as a lambda expression. – Holger Apr 07 '16 at 16:38
  • Sure, this "recursion" should point out that (at a recursion depth of 10) you will have something where `p.test(i)` is equivalent to calling 10 alternatingly nested functions (`f0(x) = { return p0(x); }`, `p0(x) = { return f1(x); }`, ..., `p10(x) = { return f10(x); }`, `f10(x) = { true }`, ). It is a true *wrapping* that takes place there. – Marco13 Apr 07 '16 at 19:50
  • That’s an implementation detail. In principle, a JRE is allowed to detect such a wrapped function and unwrap it instead of wrapping it again. It wouldn’t be too hard to implement… – Holger Apr 07 '16 at 20:00