Closed matulkum closed 2 years ago
I added a link to the rendered version so one can easily view the proposal markdown document.
As a first thought, isn't this what static extensions are supposed to solve? Why would that mechanism be insufficient? (https://try.haxe.org/#01b82)
using Test.Calculator;
class BasicCalculator {
public var result = 0.0;
public function new () {}
public function add(a: Int): BasicCalculator {
result += a;
return this;
}
}
class Calculator {
public static function sin(c:BasicCalculator): BasicCalculator {
c.result = Math.sin(c.result);
return c;
}
}
class Test {
static function main() {
var calculator = new BasicCalculator();
var result = calculator.add(2).add(3).sin().result; // <-- Totally possible in current Haxe
}
}
@EricBishton I agree that you solved this specific example with a static extensions. In general it is not the same though: you cannot override a method in BasicCalculator. You can also just add methods, not properties. You could not extend BasicCalculator with a static extension like in this example:
enum Mode {
RAD;
DEG;
}
class Calculator extends BasicCalculator {
public var mode: Mode;
public function sin() {
if( this.mode == RAD)
this.result = Math.sin(this.result);
else
this.result = Math.sin(this.result/180*Math.PI);
return this;
}
}
class Test {
static function main() {
var calculator = new Calculator();
calculator.mode = DEG;
var result = calculator.add(2).add(3).sin().result;
}
}
Also static Extensions do not help in creating Extern definitions.
I wonder if this should be a type inference feature, if you leave out the return type definition, it works? Then this
stays the same in its context, just as we have now.
@markknol Actually we do NOT have that now. Currently the return type BasicCalculator.add()
will infer to BasicCalculator
always, even if it is called from Calculator
.. and I think rightly so.
By providing this
as an option to implicitly define the return type you can have it both ways and you have actual control. See also @nadako comment at the community forum: http://community.haxe.org/t/feature-proposal-polymorphic-this-types/174/5
Just want to mention that @andyli once wrote this very nice article: https://blog.onthewings.net/2010/12/19/method-chaining-and-fluent-interface-in-haxe/
But I'd love to have polymorphic this type too, I use a lot of chaining too.
@markknol thanks for the link. Did not even see that before. So this is actually a pretty "ok" workaround for this case.
@markknol @andyli Perhaps, if Andy gives permission, a version of his article could make it into the Haxe cookbook on the web site (with appropriate attribution).
Feel free to take it to the cookbook. I'm glad that little post is still useful. :)
I'd like to also be able to do this:
class Some {
static function doSomething():this {
var inst = new this();
return inst;
}
static function doAnotherThing():Class<this> {
return this;
}
}
Which will allow to:
Maybe make it a reserved class name This
instead of this
.
We discussed this proposal and came to a conclusion it could be useful. However, @Simn has no clear vision how to implement it and needs to do a research first.
As for my previous comment about using This
as the type expression, it should be a separate proposal.
Just want to mention that @andyli once wrote this very nice article: https://blog.onthewings.net/2010/12/19/method-chaining-and-fluent-interface-in-haxe/
But I'd love to have polymorphic this type too, I use a lot of chaining too.
The link is dead. Did anyone ever add this to the cookbook? I couldn't find anything related.
@fullofcaffeine the site is up again, please PR it for the cookbook 🥇
What happens if the "this" class has type parameters?
I suppose it would always be the same as the "context"?
$type(foo) == $type(foo.getThis()); // for all concrete T
class Base {
function getThis():this;
}
class Foo<T> extends Base {}
I don't like method chaining, so I didn't care much about this proposal, but I just stumbled upon a piece of code where polymorphic This
type could help. Just leaving it here for consideration and test case if this is going to be implemented:
class BaseButton {
function setClickCallback(cb:This->Void);
}
class TabButton extends BaseButton {
function someExtraApi();
}
// and then
var button = new TabButton();
button.setClickCallback(onTabClicked);
function onTabClicked(tab:TabButton) {
tab.someExtraApi();
}
We didn't reach a consensus on this one in our haxe-evolution meeting yesterday.
There's agreement that this would be useful, but how to actually implement it remains unclear. I don't want to accept a proposal if I don't know to actually implement it, so for the time being this will remain open.
I also find this useful, but I can see a few unresolved questions:
// currently this doesn't work because function arguments are contravariant
interface Base {
function set(v:Base):Void;
}
interface Derived extends Base {
function set(v:Derived):Void;
}
// Does that mean the compiler will reject the following? interface Base { function set(v:This):Void; }
Possible solution: only allow `This` type in places where covariance is expected
2. Unification of `This` type
```haxe
class Base {
public function get():This {
return new Base(); // is this valid? If yes, it will break in derived classes, and they must override this method. Can the compiler detect this though?
}
}
class Derived extends Base {
public var derived:Int;
}
new Derived().get().derived; // runtime error: instance of Base has no field derived
Possible solution: only EConst(CIdent('this'))
can ever unify with This
type
interface Base {
function get():This;
}
class BaseClass implements Base {
public function get():BaseClass; // is this implementation valid? and how about derived classes/interfaces again?
}
Possible solution: Implementations must exactly match This
types but not the "realized" types
lean-reject: too many unanswered questions
We have rejected the proposal in the haxe-evolution meeting today.
There have been long-standing open questions that have not been answered, which we interpret as the feature potentially being "nice to have", but not important enough to the general interest. Importantly, we do not reject the feature of a this-type per-se, but rather the proposal itself on these grounds. A new proposal that aims to answer open questions could lead to further discussions, and possibly acceptance.
Rendered version