Skip to content
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

Default implementations in interfaces #70

Conversation

shohei909
Copy link

An alternative to abstract class(#69).

Rendered version

@markknol
Copy link
Member

markknol commented Mar 18, 2020

Two questions:

  • Is the first or the last argument of the interface function automagically the instance (this)? This can get confusing when the function actually has parameters. . Please complete the Util.set() function in this example. I'm personally not sure if its a good idea to allow such static access.
interface XY {
	var x:Int;
	var y:Int;
	function set(x:Int, y:Int):Void {
		this.x = x;
		this.y = y;
	}
}

class Util {
	public static function set(scope:XY, x:Int, y:Int):Void {
		// this?
		IPoint.set(scope, x, y); 
		// or this?
		IPoint.set(x, y, scope);
	}
}
  • Can you still use super in both the interface (when extending) or the overridden function in the concrete class? and can you add examples how that looks like?
@grepsuzette
Copy link

I don’t think this will work. I recommand the abstract class proposal, for a few reasons:

  1. If some method of an interface can have an implementation, it implies all of them also can. In those cases the distinction between extends and implements becomes purely formal (you would have to use implements, and yet it would actually be the same as an extension if you just changed the interface to a class).
  2. previous point implies with default implementation in interfaces, multiple inheritance would then be allowed (you can implement several interfaces); so I don’t think it’s possible.
@shohei909
Copy link
Author

Is the first or the last argument of the interface function automagically the instance (this)?

The first argument should be (this), like abstract (https://try.haxe.org/#dbEde).

So (scope, x, y) is correct.

Can you still use super in both the interface (when extending) or the overridden function in the concrete class? and can you add examples how that looks like?

I think calling interface methods with super.method() should not be allowed. Allowing it will cause confusion, when extending a class and implementing interfaces have same name method which have bodys.

interface N {
	public function sample():Void {
		trace("N");
	}
}
class M {
	public function sample():Void {
		trace("M");
	}
}

class O implements N extends M {
	public override function sample():Void {
		// `super.method()` should be only for class method.
		super.sample(); // M
	}
}
@shohei909
Copy link
Author

  1. If some method of an interface can have an implementation, it implies all of them also can. In those cases the distinction between extends and implements becomes purely formal.

In other hand, abstract class may not have implementation for all methods, and we will not able to use implements. I don't think both of them are much of a problem.

  1. previous point implies with default implementation in interfaces, multiple inheritance would then be allowed (you can implement several interfaces); so I don’t think it’s possible.

It is actually possible. The Conflict section is the solution.

@jdonaldson
Copy link
Member

jdonaldson commented Mar 18, 2020

This seems like an anti-feature to me... I don't want implementation details in my interfaces. I also don't want multiple inheritance (at least not as a default/first-class behavior). I'd really like interfaces to be simple "contracts".

If we need multiple inheritance in a library, I'd like it to use some other keyword/pattern so that the behavior there is clearly specified. Multiple inheritance code requires you to better understand the field resolution patterns at work for the resulting class. I don't want to worry about all this every time I see the "implements" keyword.

@Simn
Copy link
Member

Simn commented Mar 18, 2020

I'm with Justin on this and share the same concerns.

@shohei909
Copy link
Author

My proposal seems too short to respond to the concerns. I'll make some additions to this proposal.

@nadako
Copy link
Member

nadako commented Mar 19, 2020

I don't like this feature and I think interfaces should be pure contracts.

I can agree with one valid use-case for this: backward compatibility and I know C# recently added such feature exactly for this case, but they made it very explicit and awkward to use as a general "mixin /multiple-inheritance" feature to discourage people from abusing it. And I don't think Haxe needs this at the moment as we haven't reached .NET level of ecosystem complexity yet :) So for now, "there's a macro for that". (c)

One part that I think is somewhat useful is real static functions within an interface - it would be nice for the "factory" methods that return one or another implementation of an interface.

@shohei909
Copy link
Author

I added detail.

An important point is @:using for interface. I want to hear how do you think about it.

@posxposy
Copy link

As I remember, this feature was already rejected by the team:
#54

@Simn Simn added the rejected label Jun 18, 2020
@Simn
Copy link
Member

Simn commented Jun 19, 2020

We have decided to reject this proposal in our haxe-evolution meeting yesterday.

The reasons were already stated: We don't like having code in interfaces because it mixes concepts. While there might be some merit to this in the Java world, it is not a good fit for Haxe.

@Simn Simn closed this Jun 19, 2020
@skial skial mentioned this pull request Jun 19, 2020
1 task
@jcward
Copy link

jcward commented Jun 30, 2020

Interesting -- I have no comment on the implementation as proposed, but I do love my DefaultImplementation macro. It's one of my favorites, I'd hesitate to do a project without it.

tl,dr: I like def impl, it has its place, but it's workable as a macro (vs getting everyone to agree on one implementation.)

As for "the interface should be separate from the implementation / pure contract" -- that's one use case of interfaces. I'd suggest "the interface implies (some set of) the implementation" is a distinct and equally valid use case, that's more DRY. Whatever you call it -- decorator / mix-in / default implementation -- it's deciding that some features don't fit neatly into a class hierarchy. And interface defaults is DRY while retaining Haxe's type-safe features.

e.g. I have an IDisposable interface the provides a set of destructor features to any class I want. There's no hierarchical implications (no common base class necessary). They all provide the same interface, but the boilerplate code exists in just one place. Interfaces may be partially default-specified, leaving some functions to the class. The interface may define function implementations as @:final, whereby the class is not allowed to specify that function.

My events / pubsub classes, my controller classes, my display classes -- all implement IDisposable -- why would they have any common base class? Or, why would I want to paste the same boilerplate code into each?

It's really quite tidy. But, it works fine as a macro, and naturally, I'm partial to the way I did it. 😉 😛

@TheDrawingCoder-Gamer
Copy link

why not implement partial interfaces, but require explicit definition
this would solve the problem of interfaces being contracts, as normal interfaces are contracts, but would still allow partial interfaces to be implemented. As jcward states, this makes DRY code easier. making DRY code shouldn't require installing an external library.

@:partial
interface Example {
    public function doSomething():Void {
        trace("does something");
    }
}
@TheDrawingCoder-Gamer
Copy link

Thinking about this after learning haskell and rust, this turns interfaces into traits, which I think would be a very good thing. Traits in general imho are a better model for inheritance as it allows multi inheritance.

@TheDrawingCoder-Gamer
Copy link

why not add traits? yes, I know this is a huge feature, but that seems like the only hope for a feature like this getting in

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