-
Notifications
You must be signed in to change notification settings - Fork 545
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
Fix returning from constructors #845
Fix returning from constructors #845
Conversation
ChayimFriedman2
commented
Nov 24, 2020
- Do not allow returning with a value
- Return the instance, correctly, even when the user returned explicitly
1. Do not allow returning with a value 2. Return the instance, correctly, even when the user returned explicitly
Can you separate the 2 changes of behavior? While I understand 1 and find it interesting, I don't see what you are trying to fix with 2 and you don't provide test cases for it. |
What about foreign constructors? A way to ensure they don't return anything other than |
The foreign constructors are likely to be another subject (related though). The automatically generated static part of the constructor relies on the fact that the instance is returned as the first entry on the stack. It is good for performance, but questionable in term of security/usability. Ideally the stack should be shifted by 1 cloning entry 0, so we can safely ignore any returned values of the constructor, but it has speed implications. |
Even if interesting, I have 2 big cons about that change:
|
Previously, the following code: class C {
construct new() {
return
}
}
System.print(C.new()) Printed
This is not true: this case was already handled. See Lines 1659 to 1666 in 44d6d20
I don't understand your point, sorry.
Yes that's right: when I had to implement Detecting invalid foreign constructors is hard, because they can put what they want in slot 0. Instead I opt for trusting the user, like Wren does for most C APIs:
(from https://wren.io/embedding/). |
The point you didn't understand is about treating constructor as possible
normal function. The simple use case is method chaining.
```
class HTML {
contruct header(h) {
_header = h
return this
}
construct body(b) {
_body = b
return this
}
}
var html = HTML.header("foo").body("bar")
```
The example is a little bit crude but there might be some real usage for
it, where a function could have the same signature as a method. If this is
wanted we need to go the safe/slower route instead.
|
First, I don't understand why this PR prevents that. Second, IMO, the right way to do that is to use normal methods: class HTML {
construct new() {}
header(h) {
_header = h
return this
}
body(b) {
_body = b
return this
}
}
var html = HTML.new().header("foo").body("bar") This is how fluent interfaces are built in general. |
In wren we have a great opportunity to name every constructor as we want
and we only will use `new` and avoid to mix concepts?
|
What??? |
This is equivalent, as constructor returns this without a need to return it manually. |
Yeah, that what I meant when I said:
|
As @avivbeeri mentions elsewhere, class HTML {
contruct header(h) {
_header = h
return HTML
}
construct body(b) {
_body = b
return HTML
}
}
var html = HTML.header("foo").body("bar") This case would make that code work, but this goes back to returning random/unrelated values from a constructor which has a well defined purpose of creating an instance. |
Nope, it will create an instance with only |
|
But if you operate on the instance, you're not. If you return the class, then it's not different from creating a new instance, yup. Edit: class HTML {
construct new() {}
header(h) {
_header = h
return this
}
body(b) {
_body = b
return this
}
}
var html = HTML.new().header("foo").body("bar") And this code: class HTML {
contruct header(h) {
_header = h
}
construct body(b) {
_body = b
}
}
var html = HTML.header("foo").body("bar") Err now, but if this will be supported, they'll work. class HTML {
contruct header(h) {
_header = h
return HTML
}
construct body(b) {
_body = b
return HTML
}
}
var html = HTML.header("foo").body("bar") Doesn't err (without this PR), but ending up creating multiple instance and not setting all properties. |
Nothing prevent from pushing this change, since we can allow returns later.
The trick is that since there is no distinction in signature (ignoring
construct keyword) between a constructor and a method, I don't see a reason
why a constructor should not be usable as a regular method. The concept
comes from smalltalk where there is no such thing as a constructor:
everything code is part of regular method. The fact that we tag a method as
a construct in wren only implies that it automagically generate the static
method for us, and bind to a method.
|
I agree that this could be useful. Something like Python's However, @ruby0x1 already said that this is a bug. And even if we want to allow returning a value, we probably still want |
In all the cases it is an artificial requirement of the constructor stub.
Enforcing it now, does not prevent us from going back (unless there is a
dramatic change in constructor signatures).
|
… bit, fix extra args to finishBody
While this particular solution creates a minor edge case*, I've run into this and seen testers run into this enough times that I think having an explicit error message is better. Each time I've seen it, it caused a lot of confusion. Returning from a constructor is valid, and it shouldn't break the class instance you tried to construct when you do so. It should also make it clear when you're using the return in a constructor incorrectly, as returning a value from a constructor isn't valid - the return result is always supposed to be 'this'. Silently ignoring a return value is not as good as an error message in this case. *: |