Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reference a generic parameter recursively?

I have solved a Y-combinator problem. Just now I found that I cannot reference a generic parameter recursively.

Y = λf.(λx.f (x x)) (λx.f (x x))

for example:

IntUnaryOperator fact = Y(rec -> n -> n == 0 ? 1 : n * rec.applyAsInt(n - 1));

IntUnaryOperator Y(Function<IntUnaryOperator, IntUnaryOperator> f) {
    return g(g -> f.apply(x -> g.apply(g).applyAsInt(x)));
}

IntUnaryOperator g(G g) {
    return g.apply(g);
}

//        v--- I want to remove the middle-interface `G`
interface G extends Function<G, IntUnaryOperator> {/**/}

Q: How can I use generic parameter on the method g to avoid introducing an additional interface G, and the generic parameter should avoiding the UNCHECKED warnings?

Thanks in advance.

like image 381
holi-java Avatar asked Jul 26 '17 20:07

holi-java


1 Answers

You can declare a generic method with a recursive type definition

<G extends Function<G, IntUnaryOperator>> IntUnaryOperator g(G g) {
    return g.apply(g);
}

What doesn’t work, is to invoke this method with a lambda expression, assigning the lambda expression to G. The specification says

15.27.3. Type of a Lambda Expression

A lambda expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) …

and G is not a functional interface, but a type parameter, and there is no way to infer an actual interface type for G here.

That still works, when you use the actual interface G for the lambda expression:

IntUnaryOperator Y(Function<IntUnaryOperator, IntUnaryOperator> f) {
    return g((G)g -> f.apply(x -> g.apply(g).applyAsInt(x)));
}

// renamed the type parameter from G to F to avoid confusion
<F extends Function<F, IntUnaryOperator>> IntUnaryOperator g(F f) {
    return f.apply(f);
}

// can't get rid of this interface
interface G extends Function<G, IntUnaryOperator> {/**/}

or

IntUnaryOperator fact = Y(rec -> n -> n == 0 ? 1 : n * rec.applyAsInt(n - 1));

IntUnaryOperator Y(Function<IntUnaryOperator, IntUnaryOperator> f) {
    return this.<G>g(g -> f.apply(x -> g.apply(g).applyAsInt(x)));
}

// renamed the type parameter from G to F to avoid confusion
<F extends Function<F, IntUnaryOperator>> IntUnaryOperator g(F f) {
    return f.apply(f);
}

// can't get rid of this interface
interface G extends Function<G, IntUnaryOperator> {/**/}

So the method g is generic with no dependency to the interface G, but the interface is still required to be used as target type for the lambda expression.

like image 196
Holger Avatar answered Oct 30 '22 20:10

Holger