Lambda expression parameter can not be inferred in recursively defined type

Topics: General, Language Specification
May 6, 2014 at 4:29 PM
There are two identical implementations of method, merge1 and merge2 but the compiler thinks they have different types
class xxx<X, Y>{

    public merge1(...targets: channel<any, Y>[]) {
        var ch = new channel<void, channel<any, Y>>();
        var result = ch.flatMap(x => x);

        return result;
    }
}

class channel<S,Y>{
    public merge2(...targets: channel<any, Y>[]) {
        var ch = new channel<void, channel<any, Y>>();
        var result = ch.flatMap(x => x);

        return result;
    }

  public flatMap<R>(projector: (data: Y) => channel<Y, R>): channel<Y,R> {
        return null;
   }
}
merge1 is OK, but merge2 has an error 'infinitely expanding type reference'
merge1 has strange type - channel<channel<any,Y>,Y>

Another thing, when I change flatMap to
    public flatMap<R extends channel<Y, any>>(projector: (data: Y) => R): R {
        return null;
    }
merge1 is OK, but merge2 has the same error
merge1 now has the type channel<any,Y> as expected



Is it correct behavior of the compiler ?
May 6, 2014 at 6:23 PM
Edited May 6, 2014 at 6:55 PM
...
(edit: comment removed - was based on old knowledge, which is different now)
May 6, 2014 at 7:01 PM
Edited May 6, 2014 at 7:34 PM
The problem I see now you are saying is with the type incompatibility. This is because of how the lambda expression works - so lets see what happens when Y is replaced with channel<void, channel<any, Y>>(); and R with Y from the 'new' call ...
public flatMap(projector: (data: channel<any,  channel<any, Y>>) => channel<channel<any,  channel<any, Y>>, Y>): channel<channel<any, Y>, Y> {
     return null;
 }
So, x=>x, becomes x: (data: channel<any, channel<any, Y>>) => channel<any, channel<any, Y>> ... which is an error, because the idea of x=>x is that the PARAMETER type is the same as the RETURN type, and obviously it's not what the 'flatMap' parameter signature is returning. ;) Changing (data: Y) => channel<Y, R> to (data: Y) => Y fixes the error, but may not be want you want (but that is why the error is there ;) ).
May 8, 2014 at 9:39 PM
Edited May 8, 2014 at 9:39 PM
I agree, but there are 2 similar cases and, to me, both of them contain the same error. However, only one of them is compiled without any error.
Developer
May 8, 2014 at 9:56 PM
This should be covered by section 3.8.7 Recursive Types in the language spec. If a type is considered infinitely expanding then it does not use the normal rules for assignability and other type relations.
May 8, 2014 at 10:07 PM
Another example - member of the same class can not be compiled, but the method with identical code of different class is compiled OK
class channel<S,Y> {

static flatMapImpl<R, R1>
(self: channel<any, R>, projector:(t:R) => channel<any, R1>):channel<void, R1> {
    return null;
}

public flatMap<R>(projector: (data: Y) => channel<any, R>):channel<void, R> {
    return  channel.flatMapImpl(this, projector);
}

public merge2(...targets: channel<any, Y>[]) {
    var ch = new channel<void, channel<void, Y>>();
    var r = channel.flatMapImpl(ch, x => x); // fine !

    return r;
}

public merge(...targets: channel<any, Y>[]) {
    var ch = new channel<void, channel<any, Y>>();
    var result = ch.flatMap(x => x); // <-- compilation error

    return result;
}
}

class z<S,Y>
{
    public merge(...targets: channel<any, Y>[]) {
    var ch = new channel<void, channel<any, Y>>();
    var result = ch.flatMap(x => x); // <-- fine !

    return result;
}

}