elves / elvish

Powerful scripting language & versatile interactive shell

Home Page:https://elv.sh/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Exploding an expression: interpretation of the ‘@’ modifier

hanche opened this issue · comments

To set the stage, for a variable v, the meaning of $@v and set @v = value … is clear:

⬥ var @v = a b c
⬥ put $@v
⮕ a
⮕ b
⮕ c

Note that $@v returns what we gave to set @v.

But involve some array references, and things are not as clear:

⬥ var @v = [a b c] [d e f] [g h i]
⬥ set @v[1] = x y z
⬥ put $v     # produces expected result:
⮕ [[a b c] [x y z] [g h i]]
⬥ put $@v[1] # surprise (to me at least):
⮕ b
⮕ y
⮕ h

This breaks two reasonable expectations, IMO:

  • Expanding $⟨something⟩ should reflect the RHS of a previous set ⟨something⟩ = …
  • The construct $@⟨something⟩ acts like syntactic sugar for (all $⟨something⟩)

Of course, this depends on what you see as the ⟨something⟩ in a compound expression. What is most jarring is the lack of symmetry between the assignment and the expansion.

So this is my suggestion:

Change the meaning of $@variable[⟨index-expression⟩]… to evaluate the whole expression without the @, then explode the result(s).

Of course this is breaking change, so it needs to considered carefully. But I doubt there is much code out there that relies on the current behaviour.

  • After such a change, replacing any $@variable[⟨index-expression⟩]… by {$@variable}[⟨index-expression⟩]… will result in the old behaviour. This should be fairly easy to automate. Also, since the result of such an expression is a bit tricky to figure out, I think the extra curly braces will help to make clear that something funny is going on.

This breaks two reasonable expectations, IMO:

  • Expanding $⟨something⟩ should reflect the RHS of a previous set ⟨something⟩ = …

This is a great point.

  • The construct $@⟨something⟩ acts like syntactic sugar for (all $⟨something⟩)

As you said this is debatable.

Change the meaning of $@variable[⟨index-expression⟩]… to evaluate the whole expression without the @, then explode the result(s).

This will need to be handled as a special case and not as a result of precedence rules so I am not quite inclined to do that.

(Edit: not quite inclined)


My takeaway from this is that the entire $@x construct is really only good for one-dimensional lists, and becomes unintuitive when you go higher dimension. Rather than creating special cases for higher dimension cases, I'm more inclined to just treat them as syntax errors (so both $@x[1] and set @x[1] will be syntax errors).

I am also considering just removing the $@x syntax entirely in favor of the the POSIX syntax $x[@], which is actually superior since there is no confusion around what $x[1][@] means. What makes me hesitate a bit is that seeing x[@] on the left side of var or in function signature feels kind of wrong:

# This looks fine: $x is already a list and this is setting its content
set x[@] = a b c
 # This can work but feels wrong; $x doesn't exist yet, why is it getting indexed?
var x[@] = a b c
# One might expect this to work out of analogy, but what should it do?
var x[1] = a

# This also feels wrong...
fn {|a b rest[@]| ... }

Hmm. I also feel a bit uncomfortable with $x[@], also because @ is a perfectly fine key for a map. Sure, lists and maps are different things, but indexing them looks the same, and it feels disturbing not to know for sure what $x[@] means.

If we're investigating alternate syntaxes, I was wondering if just using $x[] might work as well? That is a syntax error currently, so it wouldn't clash with present usage. Also, it would make your two “wrong feeling” examples feel less wrong:

var x[] = a b c
fn {|a b rest[]| … }

The main argument against, perhaps, is that $x[ ] (with the space) currently has a meaning: Curiously, it expands to nothing at all, no matter what kind of value $x is. (Is that intentional, BTW?) But OTOH, spaces or the lack thereof is already significant in many places, so perhaps that is not such an important objection.

Whatever the syntax, one advantage is that this could be applied to several levels at the same time: $x[@][2][@] with or without the @.

Change the meaning of $@variable[⟨index-expression⟩]… to evaluate the whole expression without the @, then explode the result(s).

This will need to be handled as a special case and not as a result of precedence rules so I am not quite inclined to do that.

Aye, there's the rub. That is so because the @ is prefixed rather than appended, doesn't it? It took me a bit too long to figure that out.

Hmm. I also feel a bit uncomfortable with $x[@], also because @ is a perfectly fine key for a map. Sure, lists and maps are different things, but indexing them looks the same, and it feels disturbing not to know for sure what $x[@] means.

Lists already support slices too, which are also valid map keys.

The main argument against, perhaps, is that $x[ ] (with the space) currently has a meaning: Curiously, it expands to nothing at all, no matter what kind of value $x is. (Is that intentional, BTW?) But OTOH, spaces or the lack thereof is already significant in many places, so perhaps that is not such an important objection.

It means indexing $x with zero values, like how $x[a b] is the same as $x[a] $x[b]. $x[] is not useful but it shouldn't be a syntax error; that is now fixed.

Aye, there's the rub. That is so because the @ is prefixed rather than appended, doesn't it? It took me a bit too long to figure that out.

Not just that; @ is not an operator, it is part of the variable use syntax. To make $@x[y] have the semantics that you described, it is not sufficient to change the precedence. The change will be "if the indexee of an indexing expression is a variable use with @, suppress explosion of the variable and explode the value of the whole indexing expression instead".

Now that I had time to let it sink in a bit, I think I could at least get used to the $x[@] syntax. Don't know yet if I like it better than the old syntax, but it does help with the problem that got me to raise this issue.

Perhaps, though, it might even be desirable to support both syntaxes? They don't clash with each other, but it does violate a language purist's principles to have two ways to say the same thing.

In any case, I think that using explosion and indexing both on the LHS of an assignment should be made illegal, as the meaning of @x[1] changes in such a radical fashion if you put a dollar sign in front of it.

I am also considering just removing the $@x syntax entirely in favor of the the POSIX syntax $x[@], which is actually superior since there is no confusion around what $x[1][@] means. What makes me hesitate a bit is that seeing x[@] on the left side of var or in function signature feels kind of wrong:

What if setting and getting had different syntaxes? Would that feel too weird?

var @x = a b c
var @x_copy = $x[@]