-
Notifications
You must be signed in to change notification settings - Fork 58
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
Self access for abstracts #62
Conversation
I like the idea and I can totally relate to the use-cases. One thing I'm not sure about is whether we should actually generate a getter field. I would probably go with adding an identifier resolution rule instead. |
I'm obviously no compiler developer so it doesn't have to be a actual field. As long as it works that way. |
I mean, that is also an option, I'm just proposing an alternative that seems a bit more clean to me (however it might be more dirty implementation-wise). If you want to also add this to the proposal, then I'd say something like:
Although this raises more questions, as in at what place in the resolution order should it be considered, can it be overshadowed by local vars, should there be an error when declaring an argument/local var/field with the same name, etc. |
I think it should behave the same way as a field, but generate the same syntax tree as implicit cast of |
- Leave out suggested "design", leave it up to the compiler team - add duplication error example - (impr/m)ove some text
formatting
This is a good idea.
I agree. |
Isn't |
You're right, I have no idea what I was thinking. |
I like the concept well enough but just a nitpick: I struggle with the Maybe we don't need the ident to be variable – we could have a standard ident that's available in abstract contexts, something like If onsite getters land then the original code snippet would be simpler: |
What about abstract MyAbstr(MyClasss) in <ident> |
Also let's do the same thing for the underlying type: abstract MyAbstr(MyClass in <identUnderlying>) in <identAbstract> { because similar trick is also used a lot if an abstract doesn't have abstract Money(Int in int) {
@:op(A + B) @:commutative static function add(a:Money, b:Money):Money {
return new Money(a.int + b.int);
}
public function new(amount:Int) {
this = amount;
}
} |
I don't see how using |
Can we just use the |
I'm okay with the feature but it all depends on the syntax. I would not like another magic identifier so it requires some kind of declaration. Maybe something like @:this var self(get,never) : MyAbstract; |
I think my use case is related... public var red( get, set ): Float;
function get_red(): Float {
return [ 0 ];
//return read( 0 );
}
function set_red( v: Float ): Float {
[ 0 ] = v;
//write( 0, v );
return v;
} For example and more context, this experiment works, but I had to call methods because within the abstract it did not seem possible to use array access: From a users perspective it would seem logical that an abstract should allow me to clarify between self and this, I really don't like some obscure @ macro stuff. I am pretty sure if I understand Nicolas, his proposal will not be something I will forget and I will spend half an hour every time I need to use it trying to work out which subsection of the abstract documentation covers it's use or which pull request references it. I think it is far more natural to have a magic 'self'.... - it's needed for any advanced abstract use, further it should not ( ideally ) add a function call overhead as a getter setter seems to - lots of compiler magic is ideal here!! From a language design perspective abstracts are great they allow you to structure your data more like a 'Data Orientated Design' so for example I was exploring storing my WebGL positions and colours in Arrays but then using abstracts to allow easier and cleaner access and creation. I think 'self' as a keyword should be added since abstracts need them, self seems pretty clear term. Also I would still like to be able to iterate over abstract enums without having to google for a macro :) anyway just my feelings on self perhaps they will provide different perspective. |
We didn't reach a consensus on this one in our haxe-evolution meeting yesterday. We agree that this feature is useful, but the syntax remains an open question. |
I think generally abstracts suffer from a lot of 'this' compared to classes could we just use "_" to reduce noise, I think it would be really easy to remember. _[0]
this[0] For any cases where _ is not clear where it may conflict with other _ use the user can just define a local var however they want. var here =_;
var abstract =_;
var self =_; and if it has a parent abstract you can access that via var parentAbstract = _.super;
var grandParentAbstract = _.super.super; Well just adding ideas as really keen on an implemention, so maybe it will sporn other ideas. |
fair enough if your not keen on that suggestion, but I don't want self problem to be ignored. how about allowing the user to define the keyword, but they have to define it if they need self, this is not too hard to remember and it similar style to forward. @:self('myInt')
@:forward
abstract MyInt( Int ) from Int to Int { Technically this is useful because you could access 'myInt' from within a child inherited, but would need to error if an inherited abstract tried to reuse the same self. @:self('mySpecial')
@:forward
abstract MyIntSpecial( MyInt ) |
Let's not overload |
@nanjizal You can always do |
Just wanna bump the idea of using the
I think there will be no problem if we use it in expression-level for self-access? This won't break anything because even in macro it would just be What is the exact reason to reject this syntax proposal? |
We also want something for accessing |
@Aurel300 I think this suffers the same problem as Simn proposal above. Therefore Personally I see no big problem with the original proposal,
it can be misread by a newbie (confusion with And:
(I didn't know the trick detailed in the rendered version, underusing the abstract feature because of that, so I'm very much in favor of some simple solution). |
Alternatively, since abstracts requiring a This is self-documenting and awareness of that feature can be maximized by a simple paragraph in the documentation. |
Just to clarify that self access is not uncommon, I use it a lot for and wanna raise again the
abstract Error(Int) {
var Forbidden = 403;
var NotFound = 404;
// ...
public function toString() {
switch abstract {
case Forbidden: 'Forbidden';
case NotFound: 'Not Found';
}
}
public function foo(bar:Int) {
abstract.bar(bar); // to resolve shadowed field
}
public function bar(v:Int) {}
} |
Ah suddenly I understand it with your example. Find the abstract is an elegant solution, but it may be worth posting more examples e.g. with operator overloading because it falls into place so neatly all the question should revolve around the aesthetics with this solution: is it shocking or not. |
@kevinresol But. who's to say that one day, we won't have a use for |
Another time I come accross this awkwardness, and there is still no decision, it's definitely a hole in Haxe typesystem that needs plugging. https://try.haxe.org/#34fE48A9 just to clarify when I tried to switch on self compiler complained and suggested I need to cover '_' which I couldn't add or else how is the compiler helping. So I need 'self' of some form. The ( this: MyAbstract ) is really hard to remember and ugly to use for such a common situations. I think 'thisSelf' or 'thisAbstract', although both quite long, will be easy to understand for a new user. Likely I am being nieve but can the compiler not inject the required code when 'thisAbstract' is referenced, perhaps if a user need to allow dynamic/reflection access then some @keepThisAbstract or something, or perhaps 'thisAbstract' is always added but DCE will remove if not required. // implementing self to make it easier to switch.
// ideal if compiler added this and DCE unused ones away.
public var thisAbstract( get, never ): MyAbstract;
public inline function get_thisAbstract(): MyAbstract {
return ( this: MyAbstract );
} Seems very simple approach, it may not cover all bases but it would certainly go a long way. |
The simple injection would be fairly quick and easy to implement? And could be improved later if it was needed. |
If you read the previous comments you will find that the problem isn't the implementation but the syntax. |
Well I was taking Kevin's post forward and trying to get some traction that some decision is needed, and 'abstract' itself is nice but problematic. 'thisAbstract' seems clearer than 'abstract', in that there will be less user confusion. So sometimes users will want other times they will want seems really plain simple and any users of abstract types will understand that 'this' is the internal implementation and thisAbstract is ( this, MyAbstract ). I guess one other alternative...
but that feels a bit too magic. There are other options I see above suggested like 'in' and more messy '@' stuff, but to me they are overly complex for average and common use case, we would spend our time googling to remember the syntax. The situation seems very simple and outlined in the very first post by markknol, we just need 'self' and just picking a good name is needed. |
I vote anyway that next time compiler team meet there should be a decision, it's a type hole for several years now, so bumping thread a little :) But thanks anyway Simn your doing great job, even if some of us like me are annoying. |
|
I think the proposed solution is not too bad: abstract Foo(asInt:Int) { Where specifying an alternative for "this" frees up "this" to be the Foo. So kind of oldway/new way with no breaking change. |
What about |
That's the syntax I'd want but having |
While giving this another thought. A breaking change (but probably a good/correct one) would be |
Frankly that's my preference at this point as well. It's pretty silly because it's clearly backwards, but maybe I can find some mental gymnastics to justify it... |
Er, sorry, I read that backwards too. Of course I don't want to break it, but just use |
Doesn't make much sense though =/ I get the point about no breaking change, and it's an important one, but having |
While this is more "semantically correct", it could cause silent breaking change: abstract Foo({x:Int}) from {x:Int} to {x:Int} {
var x(get, never):Int;
function get_x() return 1;
public function print() {
return this.x;
}
}
({x:0}:Foo).print(); // before change: 0; after change: 1 => breaks silently |
That one seems really impractical. It would mean that you end up replacing all |
well with abstract Moo( p: Int )
abstract Foo( p: Moo ) you could reference the Int with super.super But it's horrible to break so much existing code. struggling to see how super could be made to work, found a rather ugly approach... So for current this ( Moo ) there would be two options ( old ) and ( new ): this OR this.super and for Foo the abstract one. this.abstract then you could write from within Foo to access Int : this.super.super So many users could continue to use 'this' but if they wanted more control they could use 'this.super', and 'this.abstract'. |
i really like the |
Let's have a summary of all the ideas so far… For an abstract type
|
This proposal has been accepted, see https://haxe.org/blog/evolution-meeting-2021/ for details. |
Add a way to access "self" for abstracts, which is a getter for
(cast this:MyAbstract)
.Rendered version