eclipse-archived / ceylon

The Ceylon compiler, language module, and command line tools
http://ceylon-lang.org
Apache License 2.0
398 stars 62 forks source link

Proposal for Interface Delegation #7434

Open marklester opened 5 years ago

marklester commented 5 years ago

Interface delegation would be a great addition to Ceylon. One of the drawbacks of composition over inheritance is boilerplate associated with implementing pass through methods of the interface a class wants to support. I believe this could be mitigated if the language supported Interface Delegation. The idea would be to have a keyword/annotation that allows for delegating parts of the implementing interface to child objects of a class

The statement would be in a constuctor of a class in form of:

delegate <query> to object

the query would have several forms

example:

interface Shape{
    shared formal Number area();
    shared format String name();
}

interface Rectangle extends Shape{
    shared formal Number length();
    shared format Number width();
}

class StandardRectangle(Number length,Number width) implements Rectangle{
    shared actual Number area(){
        return length*width;
    }

    shared actual String name(){
      return "Rectangle"
    }
}

class Square(length) implements Rectangle{
    value rectangle = StandardRectangle(length,length);
    delegate Rectangle::* to rectangle;
    //or    delegate Rectangle::length,width,area to rectangle;
    //or    delegate Rectangle to rectangle;

    shared actual String name(){
      return "Square"
    }   
}
CPColin commented 5 years ago

For that example code, you could do this (I replaced Number with Integer so it would compile):

interface Shape {
    shared formal Integer area;
    shared formal String name;
}

interface Rectangle satisfies Shape {
    shared formal Integer length;
    shared formal Integer width;

    shared default actual Integer area => length * width;
}

class Square(length) satisfies Rectangle {
    shared actual Integer length;

    width = length;

    shared actual String name => "Square";
}

Since you can use the default annotation for Rectangle.area, you don't need StandardRectangle.

jvasileff commented 5 years ago

@CPColin the point was to use composition, no?

drawbacks ... is boilerplate associated with implementing pass through methods of the interface a class wants to support

Yeah. But I guess a question is how bad is this in Ceylon, given the possibility of using Ceylon's abbreviated syntax? And, is doing something like Rectangle::* good, or would it always be better to list each member separately, to protect against future additions to the interface that you would want to review before simply delegating? I don't have a strong opinion on either.

For comparison, a streamlined implementation that's possible today:

interface Shape {
    shared formal Integer area();
    shared formal String name;
}

interface Rectangle satisfies Shape {
    shared formal Integer length;
    shared formal Integer width;
}

class StandardRectangle(
            shared actual Integer length,
            shared actual Integer width)
        satisfies Rectangle {
    area() => length * width;
    name => "Rectangle";
}

class Square(Integer l) satisfies Rectangle {
    value rectangle = StandardRectangle(l, l);

    // delegate to StandardRectangle
    length => rectangle.length;
    width => rectangle.width;
    area = rectangle.area;

    name => "Square";
}
marklester commented 5 years ago

The idea would be to make composition as easy as implementation inheritance. The problem seems to be a common issue: https://en.wikipedia.org/wiki/Composition_over_inheritance#Drawbacks and other languages have come up with their solutiosn for it. https://kotlinlang.org/docs/reference/delegation.html

If you had something like this I don't think you would need extend on class

guai commented 5 years ago

@marklester, kotlin is not a good example, it does not allow to delegate to its own property or some external object, but only to a constructor parameter which is very limiting I'd also prefer not to describe what to include but what to exclude in delegation declaration. like lombok does