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

Safe unpack #2617

Closed
asterite opened this issue May 19, 2016 · 21 comments
Closed

Safe unpack #2617

asterite opened this issue May 19, 2016 · 21 comments

Comments

@asterite
Copy link
Member

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?

@jhass
Copy link
Member

jhass commented May 19, 2016

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)

@ozra
Copy link
Contributor

ozra commented May 19, 2016

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.

@asterite
Copy link
Member Author

@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)

@jhass
Copy link
Member

jhass commented May 19, 2016

obj.bar? being a valid method call is not reducing confusion though IMO, on the contrary.

@asterite
Copy link
Member Author

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.

@jhass
Copy link
Member

jhass commented May 19, 2016

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.

@asterite
Copy link
Member Author

Found it! #360

@refi64
Copy link
Contributor

refi64 commented May 19, 2016

What if the syntax looks like:

a, b, c, ? = whatever # note the extra comma

@jhass
Copy link
Member

jhass commented May 19, 2016

@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.

@bcardiff
Copy link
Member

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 ?

@ozra
Copy link
Contributor

ozra commented May 20, 2016

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

@oprypin
Copy link
Member

oprypin commented May 23, 2016

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.

@sevk
Copy link

sevk commented Mar 10, 2017

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 ?

@ozra
Copy link
Contributor

ozra commented Mar 10, 2017

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...).

@refi64
Copy link
Contributor

refi64 commented Mar 10, 2017

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 _.

@refi64
Copy link
Contributor

refi64 commented Mar 10, 2017

@oprypin
Copy link
Member

oprypin commented Mar 10, 2017

@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

@RX14
Copy link
Contributor

RX14 commented Mar 10, 2017

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.

@Sija
Copy link
Contributor

Sija commented Mar 11, 2017

foo, bar? = baz conveys semantics and it's free to use, cousin of ? nilable type suffix.

@Sija
Copy link
Contributor

Sija commented May 24, 2017

@asterite Ermm... why?!

@asterite
Copy link
Member Author

@Sija Makes the language more complex, has very few use cases, can already be done with []?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants