[Proposal] Syntax sugar for Maps construction and pattern matching

101 views
Skip to first unread message

Giorgio Torres (Eugico)

unread,
Jun 26, 2023, 3:36:16 PM6/26/23
to elixir-lang-core
Hi,
I'd like to propose a syntax sugar for maps, specially for pattern matching and construction.

# Map construction
language = :elixir
%{ language }
=> %{language: :elixir}


# Map pattern match
map = %{feature: :pattern_match}
%{ feature } = map
=> %{feature: :pattern_match}
feature
=> :pattern_match

case %{ key: "some value" } do
%{key} ->
  key
  _ ->
  nil
end
=> "some value"


# Map in function args
defmodule Foo do
def bar(%{ key }),
  do: key
end

Foo.bar(%{ key: "some value" })
=> "some value"


Of course this would be just a syntax sugar. It won't compromise program's performance.

The advantage of that is mainly when pattern matching function, but also case and with statements, on maps with some considerable amount of keys.


with %{ key1 } <- %{key1: "value1"},
{:ok, %{key2, key3, key4, key5}} <-
  {:ok, %{key2: "value2", key3: "value3", key4: "value4", key5: "value5"}} do
{key1, key2, key3, key4, key5}
end
=> {"value1", "value2", "value3", "value4", "value5"}


I'd like to know if more people would be interested on that too.

Ben Wilson

unread,
Jun 26, 2023, 3:58:39 PM6/26/23
to elixir-lang-core
Hi,

This has been proposed before. Someone came up with https://github.com/meyercm/shorter_maps which accomplishes this as a macro. It might be worth a discussion again at this point but it is worth noting that this is a proposal that has been on this list before, and so it would likely be good to address considerations raised there in this new proposal.

- Ben

Wojtek Mach

unread,
Jun 26, 2023, 5:12:54 PM6/26/23
to elixir-l...@googlegroups.com
-- 
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/56ed3f2e-c696-4986-9e34-b31b720d49a9n%40googlegroups.com.

Kurtis Rainbolt-Greene

unread,
Jun 26, 2023, 5:19:09 PM6/26/23
to elixir-l...@googlegroups.com
One of the coolest value adds of a thing like babel was that literally anyone and everyone could take an afternoon to understand the plugin system and write an experiment for the language and ship it to anyone and everyone to try out. Because of the design if you wanted to remove yourself from the experimental syntax you'd just compile one last time and overwrite the experimental syntax with the regular raw syntax.



--
Kurtis Rainbolt-Greene,
Software Developer & Founder of Difference Engineers

Zach Daniel

unread,
Jun 26, 2023, 5:31:19 PM6/26/23
to elixir-l...@googlegroups.com
I think the proposed syntax  adds value and should be implemented as part of the core Elixir syntax. I also think it should not be considered for string keys, only for atom keys.

Here is a really contrived example:

`%Struct{some_xy1_stup4d_8345324_name: some_xy1_stup4d_8354324_name} = value`

`%Struct{some_xy1_stup4d_8345324_name} = value`

Those code snippets do the same thing!

Actually, that was a trick, they *don't* do the same thing, if you look closely. If this syntax was adopted more widely, I would be able to tell that, seeing syntax like the second one, the key and variable names are exactly the same. Seeing syntax like the first would likely imply to me that I should look closely as some keys may have been mapped to differently named variables.


On Mon, Jun 26, 2023 at 5:18 PM, Kurtis Rainbolt-Greene <kur...@rainbolt-greene.online> wrote:
One of the coolest value adds of a thing like babel was that literally anyone and everyone could take an afternoon to understand the plugin system and write an experiment for the language and ship it to anyone and everyone to try out. Because of the design if you wanted to remove yourself from the experimental syntax you'd just compile one last time and overwrite the experimental syntax with the regular raw syntax.

On Mon, Jun 26, 2023 at 2:12 PM Wojtek Mach <wojtek@wojtekmach.pl> wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.


--
Kurtis Rainbolt-Greene,
Software Developer & Founder of Difference Engineers

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAMhJPGgZjTQcfX4Hy%2BttQEkHhXPNLsueTNE3ijVjcQb6Rjx-9Q%40mail.gmail.com.

José Valim

unread,
Jun 26, 2023, 5:47:38 PM6/26/23
to elixir-l...@googlegroups.com
There is a very extensive proposal and discussion here: https://elixirforum.com/t/proposal-add-field-puns-map-shorthand-to-elixir/15452

There is also very clear lines draw by myself on what this feature should look like: https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/KgJwV8e-CgAJ

There have been many reasons why the proposed syntax is troublesome for Elixir. :) I am looking forward to having this discussion again, in case feelings have changed, but in the interest of everyone's time, I recommend gathering existing information before moving forward.


Austin Ziegler

unread,
Jun 26, 2023, 5:48:41 PM6/26/23
to elixir-l...@googlegroups.com
On Mon, Jun 26, 2023 at 5:31 PM Zach Daniel <zachary....@gmail.com> wrote:
I think the proposed syntax  adds value and should be implemented as part of the core Elixir syntax. I also think it should not be considered for string keys, only for atom keys.

If the proposal were to only add this matching pattern for structs, then I agree with your suggestion. However, with maps this only supporting atom keys would *practically* result in the indiscriminate use of `Jason.decode(data, keys: :atom)`.

I do not think that this would be a net positive for Elixir, even though with JS/TS I *really* like match destructuring like this.

-a
 

On Mon, Jun 26, 2023 at 5:18 PM, Kurtis Rainbolt-Greene <kur...@rainbolt-greene.online> wrote:
One of the coolest value adds of a thing like babel was that literally anyone and everyone could take an afternoon to understand the plugin system and write an experiment for the language and ship it to anyone and everyone to try out. Because of the design if you wanted to remove yourself from the experimental syntax you'd just compile one last time and overwrite the experimental syntax with the regular raw syntax.

On Mon, Jun 26, 2023 at 2:12 PM Wojtek Mach <woj...@wojtekmach.pl> wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.


--
Kurtis Rainbolt-Greene,
Software Developer & Founder of Difference Engineers

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.

Justin Wood

unread,
Jun 26, 2023, 5:53:14 PM6/26/23
to elixir-l...@googlegroups.com
For what's it's worth, I generally see people wanting this for map deconstruction instead of construction. When it comes to deconstruction, we already have a different syntax for maps with atom keys. 

foo = %{bar: "baz"}
foo.bar

Here is a really contrived example:

`%Struct{some_xy1_stup4d_8345324_name: some_xy1_stup4d_8354324_name} = value

To me, this contrived example lends itself to not supporting this syntax. Personally, if I recieved some JSON or something else that had such a long and complicated name, I would opt to somehow shorten the variable name in my code. So I would choose to not use this syntax as I would not want to have to type that huge variable name multiple times, even with the help of auto complete. 

Another point to think about is that the internals of our programs do not always translate 1:1 through every boundary in our program. UI elements get renamed, features get renamed, etc. It is much easier to just rename a map key at one of my application boundaries instead of having to rename every use of a given variable in order to support this feature. 

Just a few quick thoughts. 

Justin

Zach Daniel

unread,
Jun 26, 2023, 6:04:40 PM6/26/23
to elixir-l...@googlegroups.com
The point wasn't about not typing long names :) Or about remapping. It was about knowing when I have to know that the keys and corresponding variable names were different, and when I don't.

Ultimately I think this either lives in core or nowhere else, and that if people on a given project didn't like it they could lint it out w/ credo. I don't feel like "everyone liking a given syntax" is a realistic goal.

One thing I've seen in common in both the previous thread, the forum post, and here is that there is more agreement/interest in only supporting this syntax for structs. I think that may be a good middle ground/compromise for us to consider. It doesn't preclude us supporting *more* in the future also.


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.

Christopher Keele

unread,
Jun 27, 2023, 3:07:40 PM6/27/23
to elixir-lang-core
This is a decade-old discussion at this point, and one I've avoided participating in much because I've historically viewed the problem as untractable, and did not have much to contribute.

We want a syntax sugar construct that can interact with the variable binding scope, that visually feels like an identifier but also looks like like an atom or a string, or is only used in contexts where the identifier type is clear, or have additional syntax to support both atoms and strings. There's just not a lot of room to play with a single colon, the second-smallest possible punctuation, that doesn't ultimately look close to a tuple, which is another commonly cited issue with these proposals. Working in a single double-quote syntactically can't work because they are matched and would risk breaking existing programs by passively parsing to the next string. Any syntax sugar addition has to be a syntax error today, in all possible expressions—typespecs, function heads, etc, to ensure backwards compatibility of existing code. Sigils literals have to reproduce the full complicated parsing of the entire language to turn their strings into good AST for all edge cases, and the sigil-suffix-style clarification of atoms vs strings has never been very compelling to me. It's also unusual for sigil literals to modify variable scope, so I have never much cared for it. This is just scratching the surface of a decade of discussion!



However, I do have one new idea I haven't seen before I thought I'd bring up. The reply I linked above is in response to an interesting notion: introduce a new unary operator for this purpose. I never saw this idea get much play; the original post spitballs something like:

1. %{`foo, `bar}
2. %{~foo, ~bar}
3. %{$foo, $bar}

I realized while playing around in iex this morning:

- That we already have an existing unary operator that was not around for all of this historical discussion
- That is already a tricky power-user syntax sugar for manipulating variables in scope
- That emits a specific compile-time-error today if used on literal atoms, strings, and charlists

I am referring, of course, to the capture operator. While & is generally used as a unary operator to capture a function, within a capture we allow passing it a number to inject variables into the function scope, ex &2. What if we allowed & to capture atoms, strings, and charlists as well, only within %{} Map literals, to implement this feature? In a sense, "capturing" a variable from the environment for use in/extracting from a Map context, as well as a function context? ex:

```
foo = 1
bar = 2
baz = 3
map = %{ &:foo, &"bar", &'baz' } #=> %{:foo => 1, "bar" => 2, 'baz' => 3}
```

Trying to use in this way today yields:

```
** (CompileError) iex:3: invalid args for &, expected one of:

  * &Mod.fun/arity to capture a remote function, such as &Enum.map/2
  * &fun/arity to capture a local or imported function, such as &is_atom/1
  * &some_code(&1, ...) containing at least one argument as &1, such as &List.flatten(&1)

Got: :foo

```

We could overload it further with cases for maps. I'm not sure if complicating this already often-confusing operator is a good idea, but it feels like a good place to me, as it cleanly supports the exact literals with the types you care about, it already can modify variable scope, is already more of a power-user syntax sugar feature, and we already are committed to answering questions about it and guiding newcomers to documentation concerning it. It even syntax highlights well already.

I suspect this syntax could even be leveraged within lists to build Keywords, as well, with some more thought.

I could even see syntax for explicit struct support, either via the existing assertive access syntax (&.field) or even bare-words identifier access (&field) to encourage this particular usage. With both a struct extension and Keyword extension, you could do something like:

```
[&.host, &.port] = URI.parse("postgresql://username:password@host:5432/database_name?schema=public")
#=> [host: "host", port: 5432]
```

There are still a lot of syntax edge-cases to think through here, though.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
Reply all
Reply to author
Forward
Message has been deleted
0 new messages