eclipse-archived/ceylon

improved type argument inference

Closed this issue · 7 comments

There are some Java APIs like this one:

Stream
    .with("hello", "world", "goodbye")
    .collect(ArrayList<String>,
            (ArrayList<String> xs, x) => xs.add(x),
            (ArrayList<String> xs, ArrayList<String> ys) {
                xs.addAll(ys);
                return xs;
            });

I would much prefer to have Ceylon look at the type assigned to the first parameter of collect(), infer type arguments from that, and then let me write this:

Stream
    .with("hello", "world", "goodbye")
    .collect(ArrayList<String>,
            (xs, x) => xs.add(x),
            (xs, ys) {
                xs.addAll(ys);
                return xs;
            });

So I've implemented support for that.

The basic idea here is to break stuff down into more steps:

  1. infer the anonymous function parameter types that we can do upfront, and assign types to all the arguments that we can, then
  2. infer type arguments to the invoked function, and finally
  3. infer the anonymous function parameter types that we couldn't do in step 1.

In Ceylon we have always used curried functions to resolve this sort of problem (for example, Iterable.fold(). This work makes that idiom obsolete.

Note that in Ceylon 1.3.3, the workaround is to write this:

Stream
    .with("hello", "world", "goodbye")
    .collect<ArrayList<String>>(
            ArrayList<String>,
            (xs, x) => xs.add(x),
            (xs, ys) {
                xs.addAll(ys);
                return xs;
            });

A separate problem can be seen in the "workaround" I gave above. It would be nice to be able to write that as:

Stream
    .with("hello", "world", "goodbye")
    .collect<ArrayList<String>>(
            ArrayList,
            (xs, x) => xs.add(x),
            (xs, ys) {
                xs.addAll(ys);
                return xs;
            });

Leaving the type argument of the constructor ArrayList to be inferred. This doesn't work in 1.3.3 because function ref type argument inference only looks at parameters, not at return types. I'll fix that.

Alright, this is done, and type inference is Ceylon is now somewhat more powerful. In particular, this creates a real problem for me, because what should we do about methods like fold() now?

For example, the following code is now well-typed in Ceylon:

function fold<T,R>({T*} stream, R initial, R result(R partial, T element))
        => stream.fold(initial)(result);

value result = fold({"hello", "world", "goodbye"}, "", => partial + " " + element);

It's even well-typed if you move the stream parameter to the last position in the parameter list!

What's the problem?

@RossTate

What's the problem?

The problem is it would be a breaking change to simplify the signature of fold() ;-)

Hah, I see, that's the bit of context I was missing :)

This is done, closing and opening followup issues #7255 and #7254.