[HN Gopher] Show HN: es6_maps, new Elixir syntax feature via run...
___________________________________________________________________
Show HN: es6_maps, new Elixir syntax feature via runtime compiler
hacking
Hi HN! I thought you might enjoy this even if you don't know much
about Elixir. `es6_maps` is a small library that introduces a
"shorthand" map creation syntax, similar to shorthand object
construction in JavaScript/TypeScript. For those unfamiliar with
Elixir, the most interesting aspect might be how it achieves this.
`es6_maps` takes advantage of BEAM (Erlang's VM) hot-reload
capabilities to amend the Elixir compiler bytecode at runtime,
adding functions that expand the shorthand syntax for further
compilation. I think it's a nice showcase of the power you can
wield (with care) when writing in BEAM languages. Let me know what
you think!
Author : kzemek
Score : 58 points
Date : 2024-05-12 11:17 UTC (11 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| arrowsmith wrote:
| This is great!
|
| I've always thought that if I could change one thing about
| Elixir, it would be to add exactly this shorthand syntax that I'm
| used to from JS. I'm sick of having to type everything twice,
| like e.g. `%{foobar: foobar}`.
|
| I'm impressed to see it implemented. I didn't know it was
| possible to pass custom compilers to your Mix project definition
| - but now that I think about it, I can see how Elixir's
| metaprogramming features would lend themselves well to
| implementing custom syntax like this.
|
| Fantastic work, all I want to know is why Elixir doesn't have
| this syntax built-in already.
| dudul wrote:
| > I'm sick of having to type everything twice, like e.g.
| `%{foobar: foobar}`
|
| I'm actually very confused by this use case. How often do you
| have to do that? Fill a map with kv tuples where keys and
| values have the exact same value?
| cmoski wrote:
| the key is an atom called foobar, and the value is being
| extracted to a variable called foobar. The variable's value
| is not shown in the example.
|
| You do this quite a bit in Elixir, unless you use the .
| syntax, e.g. somemap.foobar which is marginally slower.
| dudul wrote:
| Got it - I misunderstood the short code sample, I thought
| the 2nd half of the tuple was a literal instead of a
| variable. Makes sense.
| gmassman wrote:
| This pattern appears frequently in just about all programming
| languages. I've written a lot of JS and Python, and a
| moderate amount of Elixir, and this pattern crops up quite
| often. Usually it's a side effect of complex scopes with
| several layers of function calls.
|
| Honestly I'm a little surprised you haven't seen this
| pattern. The only times it wouldn't be used are when people
| rename variables, which is frankly a practice I abhor.
| leononame wrote:
| JS and python are just about all programming languages?
|
| I'm not trying to be snarky, but e.g. I haven't seen it in
| Go, F# and Rust, some languages where I have at least seen
| _some_ code. I also don't remember a similar pattern in
| Java, C# and C nor PHP or Dart, but I will readily admit
| that it's I haven't coded in any of these in the last two
| years, so I might be a bit out of touch and I'm not super
| proficient in all those languages
| boxed wrote:
| Go, and F# do almost all positional arguments. I think
| Rust too. Java, C#, C, PHP are also positional only or
| 99% of the time. I assume Dart too.
|
| We are talking named/keyword/etc.
|
| Personally I find positional arguments to be an anti-
| pattern in all but a very specific set of circumstances.
| debugnik wrote:
| C#, F# and PHP all support named arguments and they're
| quite common for optional parameters.
| leononame wrote:
| If I understand correctly, we aren't talking about
| named/keyword arguments. This is about a shorthand syntax
| in ES6 objects, nothing to do with function arguments.
|
| In JS, when creating an object, instead of writing {name:
| name}, where "name" is both the key in the map as well as
| the variable you want it to assign to, you can do {name},
| whereas e.g. in Go you would do map[string]any{"name":
| name}.
|
| Edit: Or, for completeness' sake, it's the same with
| structs in Go, where you would instantiate a struct like
| this: Person{name: name}.
| pflanze wrote:
| If we're talking about constructing structs, like in `Foo
| { bar, baz: 123 }` (with the `bar` style shortcut in it),
| I have used that kind of syntax 10 times in 16 KLOC of
| Rust. Not a lot, but it does happen, and I found it kinda
| neat when LSP integration suggested its use.
|
| I've probably used it more for pattern matching (`let Foo
| { bar, baz } = ...`), but haven't measured the number of
| instances of that idiom.
| leononame wrote:
| You're right constructing struts in Rust works with that
| pattern, so I'll take the Rust bit back.
| cess11 wrote:
| The use of : in 'foobar: foobar' here is syntax sugar for
| :foobar, foobar
|
| where :foobar is a symbol that evaluates to itself and foobar
| evaluates to the value the variable points to.
| cmoski wrote:
| Does it work for nested maps?
| kzemek wrote:
| Yes it does!
| jzimbel wrote:
| This was done previously with the `short_maps` library, which the
| author--a member of the Elixir core team--eventually retired and
| archived due to regrets from the undesirable impacts on clarity.
|
| https://github.com/whatyouhide/short_maps
| https://andrealeopardi.com/posts/a-story-of-regret-and-retir...
| MrJohz wrote:
| That's quite surprising to me. As someone who writes a lot of
| Javascript, I usually find it easiest to read with this syntax
| than without it, and I generally recommend to my team that they
| use the shorthand where they can. This is because the meaning
| is clear and well-known, and being concise (while still being
| clear) is very valuable.
|
| I wonder what specifically the original author found made it
| more difficult to read. Was it unfamiliarity? They allude to
| some confusion between sigils and strings, so I wonder if the
| issue was partly an Elixir-specific one.
| igsomething wrote:
| The map '%{username, age, first_name, last_name}' would be 1
| character away from the tuple '{username, age, first_name,
| last_name}'. It is easy to miss the '%' character and you'll
| waste some time figuring out why your code is throwing match
| and/or function clause errors at runtime.
| aarongraham wrote:
| I really don't understand why some people are so against this
| as a language feature. I get that the community tries to stay
| away from "magic" but where is the line between magic and
| convenient syntactic sugar.
|
| For example keyword lists as the last argument to a function
| don't need brackets, and keyword lists are really lists of
| tuples where the first item is an atom. I feel like those two
| things are in some ways less obvious and more magical than this
| map shorthand.
| hhreefgguu wrote:
| Magic == "I don't like it, but I want to sound more objective
| or rational than I really am".
|
| You're definitely correct that there's as good an argument
| that these implicit features you mention are "too magical" as
| there is the the "ES6" syntax is too magical.
| kzemek wrote:
| There was also a relatively popular fork shorter_maps [0],
| mentioned in the blog post above. My motivation for
| implementing es6_maps instead of using/updating shorter_maps
| was (I'm copying from my post in the Elixir forums [1]):
|
| 1. I do firmly believe this is a good language feature and a
| very natural extension of map literals syntax;
|
| 2. at the same time, being a language feature it should be
| simple - es6_maps works only with atom keys and has no extra
| features over the key expansion.
|
| Point 1 is additionally reinforced by how easy it was to
| introduce to the compiler - I'm injecting just 9 lines of
| simple code, while parser and lexer already accept short-form
| maps without modifications.
|
| [0] https://github.com/meyercm/shorter_map [1]
| https://elixirforum.com/t/es6-maps-ecmascript6-style-shortha...
| rubyn00bie wrote:
| I personally don't think this is a good idea, and I think the
| formatter plugin is going to give someone insane amounts of
| regret one day. It's neat as "look what I did" but Elixir is all
| about removing magic.
|
| It (lack of magic) is one of the things I appreciated most,
| having originally come from Ruby and Rails. I've been using it
| professionally for nearly ten years, and I can say this isn't a
| feature I've ever really wanted. My text editor can save the
| typing via shortcuts I type, and in doing so supports string
| keys, atom keys, has zero compile time dependencies, and no new
| syntax ;)
|
| I'd really encourage you to put a note at the _top_ of the readme
| that this shouldn't be used in production code.
| karmajunkie wrote:
| i don't like adding foundational syntax using libraries, but i
| do wish they would add this to the language. deconstruction by
| pattern is super common and the syntactic sugar would be most
| welcome.
| kzemek wrote:
| > I think the formatter plugin is going to give someone insane
| amounts of regret one day
|
| The formatter plugin features a "reverse" flag exactly to
| prevent any kind of regret like that. You can reformat your
| code automatically to remove all shorthand maps and remove the
| dependency in a couple simple steps.
|
| > I'd really encourage you to put a note at the top of the
| readme that this shouldn't be used in production code.
|
| Noted, although I _do_ think it 's production ready. I'm going
| to keep updating the library if or when a new version of the
| Elixir compiler breaks it, and it does feature an easy way out
| in case I disappear.
| sb8244 wrote:
| I don't agree that this is magic. It's only "magic" because the
| language doesn't do it today (thus requiring the compiler
| extension.) If this were in the language itself, it would
| probably be pretty popular.
|
| The argument against it feels like "because we've always done
| it this way."
| oxidant wrote:
| Agreed. It's just syntax, similar to implicit keywords
| (mentioned already) and & anonymous functions[0].
|
| Pattern matching a large number of items off a map is
| verbose. Putting many new keys in a map can be verbose if
| they're existing variables.
|
| I'd rather have shorter code that was just as readable than
| many lines that add to cruft.
|
| [0] Reading `&%{foo: &1.bar}` as a new Elixir dev looks like
| character soup and is arguably more magical.
| vessenes wrote:
| A good reminder that macros give you super powers!
|
| I don't know a lot of elixir, so I can't say if this is a great
| idea for inclusion into the standard or not, but it's undeniable
| that giving macro-based access to the AST adds a lot of
| expressivity and flexibility.
| bryantwolf wrote:
| I've been using Destucture [1] for a few years. It's an
| interesting compromise that works for atom and string keys
| because it's explicit, kind of like the sigil approach. Though
| this seems way more streamlined, especially as someone who uses
| JS for front-end apps.
|
| [1]https://github.com/danielberkompas/destructure/
| kzemek wrote:
| Oh nice, I didn't know about that one! It's interesting how
| many solutions are in this space; aside from Destructure[0] and
| shorter_maps[1] I also know about shorthand[2] and synex[3].
|
| [0] https://github.com/danielberkompas/destructure [1]
| https://github.com/meyercm/shorter_maps [2]
| https://hex.pm/packages/shorthand [3]
| https://hex.pm/packages/synex
| h0l0cube wrote:
| I like this because it makes the sugaring distinct. Anyone
| unfamiliar with the sugar will know there's something different
| going on, while not really adding much noise.
___________________________________________________________________
(page generated 2024-05-12 23:00 UTC)