top | item 7162113

Big changes to Erlang

325 points| waffle_ss | 12 years ago |joearms.github.io

133 comments

order
[+] lkrubner|12 years ago|reply
I love this:

"Key := Val Updates an existing Key The key must be present in the old map. If Key is not in the old map Erlang will complain loudly. Using two operations instead of one has several advantages: A spelling mistake cannot accidentally introduce a new key"

Lord knows how many times I accidentally created a new key with a spelling typo, and then later when I wanted to retrieve the data, I was like "What the hell is wrong? I know I put data in that key, so why is nothing coming back? I guess I have discovered a rare bug in the compiler that no one has ever discovered before."

[+] rdtsc|12 years ago|reply
I also like how such rather large changes are being introduced in a language that has been in production for many decades. That is pretty remarkable.
[+] bjourne|12 years ago|reply
To counteract such bugs I usually try to use as short names as possible. I mean, there is no point in writing productionDatabase when prodDb works just as well. It has several advantages but one of them is that the latter is much less likely to be misspelled.
[+] ams6110|12 years ago|reply
But now the culprit typo is typing -> when you meant :=

Maybe less likely, but if you've been writing a lot of code using -> and then in one case need := I can see it happening quite easily.

[+] judk|12 years ago|reply
That syntax for that purpose was totally stolen from Go. Nice to see the cross pollination going in both directions.
[+] urbit|12 years ago|reply
"This trick is well known to old-style functional programmers, they waffle on about Y combinators and eat this stuff for breakfast, but it’s the kind of stuff that gives functional programming a bad name. Try explaining this to first year students who had a heavy night out at the pub the evening before."

Priceless (and completely true). I really think this attitude is why Erlang is such a success.

[+] tel|12 years ago|reply
Meh, the Y combinator is a funny hack. A lot of people waffle on about it because they're blown away by how crazy untyped lambda calculus is. There are all kinds of ways to introduce recursion into a language, though. It's simply interesting that you might get it on accident in a language will as little restriction as UTLC.
[+] thirsteh|12 years ago|reply
I think there's definitely a rule against dismissing Y combinators on news.ycombinator.com.
[+] dschiptsov|12 years ago|reply
There are two side notes. 1. Should we really be constrained by frequent pub visitors when teaching CS? Adapting to stupidity (OK, mediocrity) caused Java and switch from Scheme to Python for intro courses at MIT (with great help of mr. Guttag, I suppose). 2. Should we ever hire a person who couldn't get what Y Combinator is or not being able to write one in Scheme?

Update: Python is heavily used in Biology and related research (so they said) and transition at MIT seemed like reasonable, but I still believe that Scheme shall be the language for serious introductory courses, like classic Berkeley CS61a was. Now PLT/Racket folks are doing great job of introducing proper concepts and developing "good habits" to students.

[+] gordonguthrie|12 years ago|reply
I am in the process of writing an OTP-ish dialect of Erlang that compiles to Javascript and has its own runtime environment (http://luvv.ie/mission.html) so maps is a more work for me, so I am a bit yay!/sigh!...

The thing that I am less clear about with maps is their relationship with Mnesia tables going forward. Mnesia tables take their schema from records. I suspect we will go down the route of having records that contain 'privileged' fields (effectively ones you can have indexes on) and field that contain maps which allow to 'extend' the schema without performing difficult state transforms.

It will certainly help when you need to add a field to a record and thread that change through a running system without stopping it.

[+] rdtsc|12 years ago|reply
Hey, luvvie looks awesome. Thanks for all your great work. I am following it. Sorry don't have much insight as to what happens with Mnesia. Just wanted to thank you for your work.
[+] portmanteaufu|12 years ago|reply
Whoa. This makes the language feel much more approachable. I love that they've provided both an "upsert" and a semantically distinct "update" for their maps. Working without upsert is painful, but the problem of accidentally inserting when you meant to update is an irritating pitfall. They've really picked a good middle way here.

Does anyone know how often Learn You Some Erlang is updated? I'd definitely take another pass at the language with these features in place. Kudos, team!

[+] mononcqc|12 years ago|reply
Author here. I haven't yet updated it, but plan to eventually update the website with these features. The thing is a lot of it won't need to change majorly.

Maps should be a replacement of data structures like dicts and gb_trees, but I personally do not see them as a replacement of records within a module, where I feel their restrictiveness is welcome, for two main reasons:

1. especially to crash early in live code upgrades, despite, I'm sure, a lot of people disagreeing with me.

2. The module isolation inherent to records makes people think at a protocol level and with their API much, much better than the common pattern of saying "screw it", sharing the state around, and breaking abstraction all over. I like how it constrains the programmer to think of what should be passed around in messages, and that maps may remove that "think hard" part of the problem.

Maps should be especially nice and enable more complex dictionary manipulations, nested key/val mapping, and so on, and in terseness of operations. More elegantly, they could be a decent fix to 'use ETS to optimize K/V operations', although they won't benefit from the same parallel access.

I plan to explain this and possibly revisit some code snippets from the book in an add-on chapter, and also show what I wouldn't change.

Regarding Funs, I probably will just add a section to the anonymous function part, and see if I ever used recursive anoynmous functions before. It's likely that I avoided them on purpose in the book and as such, won't need to add too much there.

Let me know if that sounds good to you.

[+] sandal|12 years ago|reply
Not a direct answer to your question but Joe's "Programming Erlang" book has been fully updated, and includes a chapter on the new map feature.
[+] Groxx|12 years ago|reply
>If we now build a large list of objects, all with the same set of keys, then they can share a single key descriptor. This means that asymptotically maps will be as efficient in terms of storage as records.

Credit for this idea comes from Richard O'Keefe who pointed this out years ago. I don’t think any other programming language does this, but I might be wrong.

That is a nice language feature to enforce, I love it :) This is however basically the same thing that V8 / most Javascript runtimes do under the hood[1]. Essentially, as you construct objects and add/remove keys, you back them in the engine with more-efficient shared objects that have the same structure. Add a key D and it moves from <shared structure with A,B,C> to <shared structure with A,B,C,D>. Since most code ends up doing the same kind of operations on a bunch of similar bits of data, you can save a lot of compute-time by assuming that and having a slower fallback when it fails (like using an actual dictionary instead of a Struct-like thing).

That said, Javascript has zero enforcement for this, and the runtimes may have already shifted to something different. They are quite different beasts. Just pointing out that the idea has been around.

[1] it has been a while since I've looked closely, and I may be mistaken. Call it 95% certainty.

[+] Cushman|12 years ago|reply
These are great changes, and a reminder that I must find a reason to write more Erlang.

For the record, one of these is another thing JavaScript got right, sort of, with named function expressions:

  setTimeout(function innerName() {
    if (shouldRecurse())
      innerName();
  });
  typeof innerName; // undefined
I don't think I've ever seen anyone actually use this in production code, of course. And you're free to have your own issues with function declarations and named function expressions having the exact same syntax, which causes a big problem in IE<=8 that is probably responsible for this feature not being in common use (and omitted from CoffeeScript, alas).
[+] masklinn|12 years ago|reply
You can also do that in Clojure, since you'd usually self-recurse with (recur) and a function will create a recursion point.

    (set-timeout (fn []
      (if (should-recurse)
        (recur)))
[+] sdegutis|12 years ago|reply
It seems odd that we haven't standardized on a name for maps/tables/dictionaries/associative-arrays/hashes.
[+] colanderman|12 years ago|reply
Well, most of those really conflate two things: static heterogeneous "maps", and dynamic homogeneous maps. The former being a structure whose keys are known at compile time (static) and whose values may be of different types (heterogeneous); the latter being a structure whose keys are not known until run time (dynamic) and whose values must all be of the same type (homogeneous).

Most languages conflate these, because they kind of look the same, but they are entirely different use cases, with different use patterns, permit different optimizations, and their generalization (a dynamic heterogenous structure) is pretty useless.

The relational model [1], which has been around since 1969, does distinguish these. The static heterogeneous structure is called a "tuple", and the dynamic homogeneous structure is called a "relation".

If only language designers would stop conflating the implementation of these structures with how they are used, we could drop the cringeworthy names "associative array" and "hash", stop calling tuples "maps" (which is a specific kind of relation), and stop calling relations "tables" (which are two-dimensional arrays, which are another kind of relation).

Erlang at least as of R17 has mostly kept this distinction (Erlang maps mostly support the static heterogeneous use case). However the majority of users are keen to jump on the "maps" bandwagon as a way to get syntax support for dynamic homogeneous use (which makes utterly no sense to me, as most of the syntax is for pattern matching and construction, which makes little sense in the dynamic case!). I am glad R17 does not include many of the proposals for such syntax that I've seen on the mailing list; and instead sticks closely to the original static heterogeneous design proposed by Richard O'Keefe.

[1] http://en.wikipedia.org/wiki/Relational_model

[+] shadowfiend|12 years ago|reply
Well, hashes in particular implies a very specific kind of map/table (one that uses hashing). I think the most common non-implementation-dependent names are maps, dict(ionarie)s, tables, and associative arrays.
[+] kevinmchugh|12 years ago|reply
And in JS, it's an object.

Figuring out how to store arbitrary key-value pairs should be one of the first steps in learning a new language, given that every language has a different name for it.

Associative Array is probably my least favorite name for it, since it implies arrays aren't associative. It was a clojure book that first made me realize an array is a map that only allows ints for keys.

[+] kibwen|12 years ago|reply
As someone who's completely foreign to Erlang, can anyone explain the rationale behind replacing records with maps? At a first glance it seems as though records are nominal types and maps are structural types, and if true that would suggest to me that these are complementary features rather than supplementary ones.
[+] masklinn|12 years ago|reply
> As someone who's completely foreign to Erlang, can anyone explain the rationale behind replacing records with maps?

Simplicity and flexibility. Erlang records have to be declared and they're nothing more than syntactic sugar for tuples.

> At a first glance it seems as though records are nominal types

They're not, not in the way you'd usually expect anyway. They're little more than macros for accessing tuple fields by name:

    -module(test).

    -record(foo, {bar, baz}).

    main(_) ->
        R = #foo{bar=1, baz=2},
        io:format("~w~n", [R]).
will print:

    {foo,1,2}
the record information does not exist at runtime. In fact, you can just create the corresponding tuple and tell Erlang to interpret it as a record:

    S = {foo, 2, 3},
    T = S#foo{bar=3},
    io:format("~w ~w~n", [S, T]).
will generate no warning from either erlang itself or dialyzer, and will print

    {foo,2,3} {foo,3,3}
And although dialyzer (erlang's "type checker") can use records as type specs, it will be updated to handle map specs[0], allowing for the exact same static expressivity since you can define a map with explicitly specified keys e.g.

    -type foo() :: #{ 'status' => 'update' | 'keep', 'c' => integer() }
[0] http://www.erlang.org/eeps/eep-0043.html section "Dialyzer and Type specification", sadly not directly linkable.
[+] kabdib|12 years ago|reply
The templating of the key descriptors is neat, but not original. NewtonScript did this in the early 90s (mostly in response to the severe memory pressure on the Apple Newton). I believe that Self did it, too (but those guys were on Sun workstations, and just showing off as far as I'm concerned :-) )
[+] leakybucket|12 years ago|reply
I've got a passing interest in Erlang, and also saw the existence of Elixir http://elixir-lang.org/ , which uses the Erlang VM. Could someone from either language's community comment on if they see a strong future for Elixir?
[+] rdtsc|12 years ago|reply
It runs on Erlang's VM. So there are chances if you pick one up or another you'll be able to at some level inter-operate.

It is up to you, try one and another one. Some people have big issue with Erlang's syntax. I actually kind of like it, so I prefer Erlang.

Others find Elixir more approachable, it reminds them of Ruby for example. Elixir has macros so you can do some nifty things with them. Those kind of blow my mind when I read them so I am afraid I would get too "clever" with them.

You can call Erlang from Elixir fairly easily so you can take advantage of libraries you find for Erlang.

So kind of up to you. Whatever you like or whatever gets you more productive and interested.

[+] NDizzle|12 years ago|reply
This is great. I can attack the few chapters I skipped over in Joe's most recent book[1] that required R17.

[1]: http://pragprog.com/book/jaerlang2/programming-erlang

[+] fidotron|12 years ago|reply
I have to say, this is one of my favourite programming books, even though I've never been paid to write any Erlang at all. Just the insights into how to go about building reliable systems are inspiring and profoundly affected my work in other languages.

That and it's written in a brilliantly approachable style.

[+] Buttons840|12 years ago|reply
Thank you for mentioning this book. I intend to learn Erlang after I finish studying Haskell, and I'll probably use this book because of your recommendation.
[+] talklittle|12 years ago|reply
EEP-43 [0] standards draft, with an extensive overview of new Maps features, plenty of examples, and discussion on a few different syntax proposals they had.

(Is there any newer official documentation for maps? I couldn't find anything.)

[0]: http://www.erlang.org/eeps/eep-0043.html

[+] pointernil|12 years ago|reply
Kudos to all ppl involved! Thanks for the effort.

Compared with maps adding a fun-call chaining operator like |> should be child's play, right? ;)

Seriously, what are the functional programmer's tricks to handle long function call chains in languages without the |> or similar operators?

Anyway, maps are a more important addition to the environment for sure. So thanks again.

[+] nox_|12 years ago|reply
An F#-like operator |> would be useless without a convenient partial application syntax. Macro-like operators aren't well received in Erlang, certainly not when they obfuscate what really is going on.
[+] oinksoft|12 years ago|reply
Thanks to Anthony Ramine for his hard work on the named `fun' feature, and to the OTP guys as always :^)
[+] sbierwagen|12 years ago|reply

  code {background: #F7F7F9; color: #999999}
Egad, the color scheme he uses in the code blocks is horrible. The snook.ca contrast calculator says that's only a 2.66:1 contrast ratio, which is terribly unreadable. It needs to be at least 7:1.

(CSS edited for clarity)

[+] colanderman|12 years ago|reply
There's been lots of chatter about maps on the mailing list; but the named inline functions thing is totally out of the blue to me, and totally welcome. Erlang does such a great job of fixing everything that OCaml almost but didn't quite get right.
[+] nox_|12 years ago|reply
Implementor here. Richard O'Keefe deserves all the credit as he had the idea and suggested the syntax (EEP 37). I just noticed that it was easy to implement it through let rec expressions in Core Erlang. No VM changes were required.
[+] siraaj|12 years ago|reply
Could you elaborate on what you feel OCaml didn't quite get right WRT recursive definitions?
[+] brickcap|12 years ago|reply
Maps look really interesting. They can also be saved in mnesia right?
[+] nodivbyzero|12 years ago|reply
Way to go, Erlang! As a developer, I really appreciate it.
[+] varg|12 years ago|reply
Great! Maps looks really nice :) Will mnesia be extended to us maps as an alternative to records?