New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support `Self` type with extension methods #26

Closed
YounesRahimi opened this Issue Nov 11, 2018 · 4 comments

Comments

Projects
None yet
2 participants
@YounesRahimi

YounesRahimi commented Nov 11, 2018

Is There any way that we could write extension methods for generic types? For example:

    public static <T> T println(@This T thiz) {
        System.out.println(thiz); 
        return thiz;
    }

Or

    public static <T extends Collection<? extends Long>> T printMax(@This T thiz) {
        System.out.println(thiz.max(Long::compareTo));
        return thiz;
    }

Thanks again for this awesome library.

@rsmckinney

This comment has been minimized.

Member

rsmckinney commented Nov 11, 2018

Extension methods do support generic types. For instance, you can write an Iterable extensions like this:

package extensions.java.lang.Iterable;

public static <T> T first(@This Iterable<T> thiz) {
  . . .
}

In your examples, however, you attempt to extend a type variable T, which is not an addressable type.

The essence of what your example methods accomplish is called the Self type. The Self type is used primarily to support covariance on return types declared in a superclass. Although Java does not support the Self type, it can be accomplished indirectly via recursive generic types. For example, if java.lang.Object were to provide your println() method it would do so as a recursive type:

public class Object<T extends Object<T>> {
  public T println() {
    System.out.println(thiz); 
    return thiz;
  }
}

As such a subclass of Object is declared like this:

public class MyClass extends Object<MyClass> { 
}

Now you can call println() with covariance:

MyClass myClass = new MyClass();
myClass = myClass.println(); // return type is MyClass

If Java supported the Self type, you could achieve the same thing without resorting to generics:

public class Object {
  public Self println() {
    System.out.println(thiz); 
    return thiz;
  }
}

Now this begs the question, can Manifold provide Java with the Self type? I believe so. This will be an interesting feature to implement...

@rsmckinney rsmckinney changed the title from Support Extension methods on Generic Types to Support `Self` type with extension methods Nov 11, 2018

@rsmckinney

This comment has been minimized.

Member

rsmckinney commented Nov 22, 2018

Fixed with af9746b

Watch the Self Type documentation for details

@rsmckinney rsmckinney closed this Nov 22, 2018

@YounesRahimi

This comment has been minimized.

YounesRahimi commented Nov 24, 2018

Thanks for this nice feature.
There are some limitations to use @self. For example, if we want to pass a lambda to extension function which takes a parameter of the type of @self it won't compile:

    public static <T> @Self Object let(@NotNull @This Object thiz, Function<@Self Object, T> mapper) {
        return mapper.apply(thiz);
    }
@rsmckinney

This comment has been minimized.

Member

rsmckinney commented Nov 24, 2018

Hi Younes.

This is by design; the @Self annotation is limited to a method's return type (for now). The compiler produces an error message if used elsewhere.

A key difference with Manifold's self type is although @Self targets the method declaration, it resolves at the method call site -- the best of both worlds. As a consequence it avoids the barrel of monkeys that is Java method bridging and, importantly, it enables more flexibility with the type system in terms of method signature variance and generics. This opens the door to type-safe usages in methods like Object#equals(Object) -- with @Self you could instead define it as: Object#equals(@Self Object) where you can override the method using the same @Self Object parameter type, but still have the compiler enforce the subclass type for arguments to the method as well as enforce subclass treatment of the parameter in the method body. This feature will be available in a future release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment