Has Map shorthand syntax in other languages caused you any problems?

Map shorthand syntax has been proposed for Elixir many times. The support for adding it seems generally positive, but there are people who express concerns. I have wondered when reading those objections about whether they are theoretical concerns from people who have never used the shorthand or people who have used it and have horror stories about all the ways it can go wrong.

For those unfamiliar, what I mean when I say Map shorthand syntax is this (example in javascript):

let x =1;
let y = 2;
let point = {x, y}; //shorthand creation
let polarPoint = {r: 1, t: 2};
let {r, t} = polarPoint; //shorthand assignment

Which would be equivalent to:

let x =1;
let y = 2;
let point = {x: x, y: y};
let polarPoint = {r: 1, t: 2};
let {r: r, t: t} = polarPoint;

The main objections I’ve read are:

  1. It is less explicit. It is not obvious what is going on. It feels like magic.
  2. The variable name has become meaningful. So, for example, if you change the key name, you would also change the variable name.
  3. People may overuse this, with the implication that some of those uses are bad.

What I would like to know is:

  1. Have you used this feature?
  2. What language?
  3. What problems did you have? or why were the problems other people had not a problem for you?
4 Likes

I love it in JavaScript and use it all the time. But I don’t think it would be the right thing for Elixir … it doesn’t feel like a pattern that would fit in very well.

4 Likes

Have you used this feature?

where possible.

What language?

JS / I am working mostly with React

What problems did you have? or why were the problems other people had not a problem for you?

I don’t know about any problems at least in modern JS. Obviously it will not work with key names, that are not mappable to valid variable names like

const { hello-world } = { "hello-world": "Hello World!" } // won't work..

But its common to use camelCase keys in objects so, I would say it really helps in React Projects, where you have a lot of functions which receive objects as params like a simple PureComponent:

const MyApp = ({ appName, version, currentUsersCount }) => {
    return (
      <div>
         <h1>Welcome to {appName}!</h1>
         <p>
           You are one of our {currentUsersCount} Users.
           This is version {version} of {appName}.
         </p>
         <h3>Have fun!</h3>
      </div>
    );
}
1 Like

The main issue here is to decide what type of keys should be used for the map.

Elixir macros are very flexible, if you really need this feature you can use https://github.com/meyercm/shorter_maps

2 Likes

Thanks @fuelen, but I wanted to understand problems with the shorthand syntax itself regardless of language. I understand that Elixir has additional complexities around keys and I know there are libraries that attempt to solve it like short_maps, shorter_maps, shorthand, and synex. I would like to understand if there are objections that people using shorthand syntax experience in practice in other languages that we would be bringing into Elixir if we added it.

If you need map with values that are same as keys, wouldn’t it be simpler to just use tuples?

2 Likes

Can you expand on that?

The problem with tuples is that they’re positional and when you pattern match them, you have to match the whole structure. With maps, the order doesn’t matter, because you can pull out the values by name. You can also match on a subset of the entire map. It’s rare to see tuples larger than 3 elements. Erlang created records to deal with this problem before maps existed in the language. Ironically, the first discussion that I’ve found talking about adding shorthand to elixir was to use with records.

map = %{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}
tuple = {1, 2, 3, 4, 5, 6}

%{e: e} = map
{_, _, _, _, e, _} = tuple
1 Like

I’ve used this syntax in javascript and you enumerated all of the issues that I have with it there as well. It encourages a specific style of programming that I think leads to highly coupled code without any indication that its highly coupled. If you change a key in a json object that you’re passing around through all of your functions then you now need to 1) find all the functions that relied on that key and 2) update the code inside the function that also relied on that variable name. I understand why people like the brevity but I don’t think that brevity provides much benefit in the long run.

6 Likes

Thanks for responding. I would make the argument that the key name and the variable name are already coupled, in that both are describing the value that they point to. So, if the value they point to changes, such that the name no longer makes sense, I would want to change both. I personally would make that sort of change regardless of shorthand syntax being present.

I suppose you could mean, when the names semantically are similar, they are changed to be the same. I think in this case, you could take the conservative approach and just change the variable name and use a key value pair to give them separate names, because it reduces the things that must change. However, if someone chooses to make the names the same universally, are you saying the code is worse off because of that? Wouldn’t you have the same amount of coupling even if shorthand were not used since various other places depend on that maps key?

If you change a key in a json object that you’re passing around through all of your functions then you now need to 1) find all the functions that relied on that key and 2) update the code inside the function that also relied on that variable name.

I dont understand how your point is related to shorthand syntax…

If your code is either this

function foo(obj) {
  console.log(obj.bar)
}

or this

function foo({ bar }) {
  console.log(bar)
}

And you change the key bar to baz, you have to search and update it anyway… or not?

Indication of coupling could be provided with a typing system like TypeScript

interface MyObject {
  bar: string
}

function foo({ bar }: MyObject) {
  consoel.log(bar)
}

Unfortunately there are no out-of the box typing in JS, but on the other hand in Elixir we have types, so the coupling problematic wouldn’t apply.

Lets say with structs, this would be really awesome:

def foo(%MyStruct{ bar }), do: IO.puts bar
2 Likes

My point is that in the case of elixir the coupling is obvious. The javascript syntax is highly non-obvious to me. This is a highly personal and subjective opinion but that’s what this entire thread is about so I guess its fine :wink:.

My argument isn’t that pattern matching on elixir maps today isn’t coupled to the call site. It is. But shorthand syntax makes that coupling even less obvious.

Are you talking about dialyzer or something else? I’d probably put dialyzer in the same category as TypeScript or Flow. Dialyzer is highly underutilized in my experience.

Maybe I’ve just been writing elixir for too long but I don’t understand the appeal of this. Removing explicitness for the sake of brevity doesn’t appeal to me.

4 Likes

I’m using it in JS and tried in Rust while read TRPL. It works there because keys can be only of one type in both of those languages.
Elixir as you know is a different story.

@keathley same observations here! very well put, thanks.

I tolerate this in JS but it’s not my favorite feature :slight_smile:

If such thing were added to Elixir we’d have another way to do exactly the same thing on a syntax level, which in my opinion is never good.

This feature would arguably improve readability of code if it’s read like English - what it does or should do, what’s its intent. The trade off would be the “real” readability - what happens. To put it other way: when I scan through the code I want to know for sure and as quickly as possible: this is a function call, these are the parameters, it’s a list, it’s a map, it’s a tuple etc. - such feature would hinder this ability.

So yes, the first of listed points is the deal breaker in my opinion :slight_smile:

1 Like

@lessless If you’re using it in JS and having the same problem @keathley described, why are you still using it? Or did you mean you have used it, but don’t anymore?

I’ve used such shorthand syntaxes in quite a variety of languages such as a few ML languages, Haskell, even C++ libraries.

In Elixir I could see it working as such:

%{a, :b} = %{"a" => 1, :b => 2}

1 = a
2 = b

It defaults the most common case to be string and atoms equal atoms in it. Not perfectly happy with it by any stretch, but it seems like a good ‘common case default’ and supports mixed values and all just fine (unless you have the same string value of a string and atom key, though you might have other issues in design then).

1 Like

@OvermindDL1 did experience any of the problems people here mentioned? or any of your own?

If not, do you have an opinion on why those things weren’t problems for you?

@yurko Do you have a similar problem with keyword lists not needing to be wrapped in [] when they’re the last item? I would classify that as a similar feature to map shorthand.

foo(1, 2, x: 1, y: 2)

{1, 2, x: 1, y: 2} #=> {1, 2, [x: 1, y: 2]}

I’ve added a proposal for field puns here: Proposal: Add field puns/map shorthand to Elixir

Honestly, this is one thing that trips up everyone that I’ve taught JavaScript to. Some are new and some are “old” javascript, but neither understand the syntax right away. Those that have seen pattern matching think its that and are confused why nested destructuring (honestly never heard “pun”) fails. This will just add complexity to the language to save a few keystrokes for advanced users, when I’d prefer to have more newcomers picking up the language.

2 Likes