Right now this:
x, y, z = exp
is translated to this:
temp = exp
x = temp[0]
y = temp[1]
z = temp[2]
This is OK, but sometimes we aren't sure if exp
will have values for all indices. For example, an HTTP response status line may consist of two or three pieces separated by newline. Right now we have to do something like this:
pieces = line.split
version, code = pieces
message = pieces[2]?
# etc.
It would be nice to have a way to fetch elements at a given index with []?
instead of []
, to get a nil
value instead of an exception.
An idea I had for some time is to use ?
on the left-hand side:
version, code, message? = line.split
The above would be rewritten to:
temp = line.split
version = temp[0]
code = temp[1]
message = temp[2]?
We could of course also use it in block arguments:
some.each do |(x, y?)|
# ...
end
I believe all of this can be useful and makes code more concise and clear, with less checks. I'm still not sure about the syntax, though, it might look confusing and it's yet another thing to know in the language. But a few times I felt I needed this in some code.
Thoughts?
I like the general idea, but I have doubts about the syntax too. Since ?
is a valid part of identifiers elsewhere, this is rather confusing.
Following #132, one mitigation could be allowing
pieces = line.split
(version, code), message = pieces, pieces[2]?
Another alternative I see is having a Array#values_at?
or even Enumerable#values_at?
returning a tuple. That would require macros that are callable on instances.
version, code, message = line.split.values_at?(0, 1, 2)
Being able to take any source, without need for special calls there (you might get, say, an array in an arg), and define optionality on the receiving destructured variables looks cleaner to me, whether ?
or something else is used. The partially grouped multi-assign looks less comprehensible than that ?
has a different role in destructuring (or multi-assign as it's called here) - afaic.
@jhass True. On the other hand, using foo?
as a variable currently works but it isn't intentional. For example this in Ruby gives a syntax error:
irb(main):001:0> foo? = 1
SyntaxError: (irb):1: syntax error, unexpected '='
foo? = 1
^
Not that we have to do the same as in Ruby, but I think disallowing question marks and bangs in variable names is sane.
With that, there would be no problem in using this syntax. It looks weird, maybe because it's not present in Ruby, but the use of ?
is consistent with other parts of the language.
Then of course this would also be possible:
# This is already possible
obj.foo, obj.bar = exp
# This would be possible with this feature
obj.foo, obj.bar? = exp
Again, obj.bar? = exp
is not valid syntax, so there's no conflict (this last use case is not very common, though, but it still can be supported)
obj.bar?
being a valid method call is not reducing confusion though IMO, on the contrary.
The point is that obj.bar?
can't be assigned a value, there's no way to have a bar?=
method in the language (though it seems I'm wrong :-P). This last snippet should also be invalid in my opinion.
I feel like bar?=
has come up before somewhere, but Githubs issue search ignores ?=
so it's hard to find :/ I think it was in the context of obj.bar? ||= value
somewhere.
What if the syntax looks like:
a, b, c, ? = whatever # note the extra comma
@kirbyfan64 I think that the ?
relates to the c
is non-obvious that way. What however also crossed my mind is adding an operator for it that would apply it to the whole expression
version, code, message ?= line.split
# or
version, code, message =? line.split
# or similar is rewritten to
_tmp = line.split
version = _tmp[0]?
code = _tmp[1]?
message = _tmp[2]?
Though I don't think I like that much more.
I like the idea. What if the ?
is a prefix and not a sufix?
version, code, ?message = line.split
identifiers can't begin with ?
so there will be no ambiguity.
Another alternative is that, the ?
is used to mark a _from now on_
a, b, ?, c, d = line.split
So c
, d
will be nillable.
Is there a scenario where c
can be nillable but d
no?
If so,
a, b, ?, c, d = line.split
or
a, b, ? c, d = line.split
might also be a solution the ambiguity if the trailing ?
Hey, that "from now on idea"! Might play well for all of us? :-)
Now, if we also had key-destructuring, then it's another story though (we don't atm of course), then this makes less sense:
some_mapish_thing = {some_key: "Un", unused_key: "Dos"}
# here `var: key` could be defined opposite in syntax ofc
(my_var: :some_key, ?, var2: :other_key) = some_mapish_thing
You call this "safe" unpacking but I think having too many items and silently ignoring them is much more dangerous than a runtime error when there are too few items. And the code for catching this is not so nice, compared to just having to write = a[0], a[1],
a[2]
Ideally, I would like the default to be a runtime ( compiletime for tuples?) error when the number of items doesn't exactly match, and then whatever other syntax options for leniency you might like.
a=[]
a[99] #=> nil
a={}
a[99] #=> nil
nil.to_s #=> ""
nil + "9" #=> "9"
nil + 9 #=> 9
a[9]="ok"
a["9"] #=> "ok"
9 + "9" #=> "99"
"9" * "9" #=> 81 #because string has no * operator
I like a language like this , and syntax like ruby , and performance like cpp .
how about this ?
I like a language like this , and syntax like ruby , and performance like cpp .
"Pick one"
@sevk - The comment is completely unrelated to the issue, and the proposals are Javascript-Batman'ish (NaNNaNNaNNa...).
cough Back on topic... cough
Would something like Python's syntax work?
a, b, *rest = (1, 2, 3, 4)
# a: 1
# b: 2
# rest: (3, 4)
Then you could ignore the extra by, say, assigning it to _
.
@kirbyfan64, that's how it should be for sure, but unfortunately the conversation isn't even going that direction.
Currently a, b = {1, 2, 3, 4}
works without any error. And they want some way to make a, b, c = {1, 2}
to work without error, possibly by adding a ?
to c
I do think that adding *rest
support for destructuring would make *_
a very natural syntax for taking the first and last bits of a variable-length destructure. Additionally, I'd tentatively support ensuring that the LHS and RHS of destructures have the same size. However, both of these topics seem to be orthogonal to this issue.
I'd support either foo, bar? = baz
or foo, ?bar = baz
syntax personally.
foo, bar? = baz
conveys semantics and it's free to use, cousin of ?
nilable type suffix.
@asterite Ermm... why?!
@Sija Makes the language more complex, has very few use cases, can already be done with []?
Most helpful comment
@asterite Ermm... why?!