Comments to the book "Learn You Some Erlang For Great Good" by Fred Hébert, Chapter 3 - Syntax in Functions. Pattern matching is much easier to achieve in Erlang than in many other languages, but the syntax takes some time to get used to. Instead of if-else or similar conditional branching, in Erlang you simply use functional declarations with a pattern. Learning goals: Being able to create pattern matching, retaining unbound vs unbound variables, and how to work with guards. Challenges: Differences between unbound vs unboundvariables. Fully understanding how to work with guards.
This article series has come about as a result of my efforts to learn Erlang. In order to learn the language, I am reading the book "Learn You Some Erlang For Great Good" by Fred Hébert. Every time I come across something that I find difficult to understand I will make an effort to understand it and explain my understanding in detail. I will also - as far as possible - site alternative sources, possible exercises and more. First off, maybe you could be a pal and buy Fred Herbert's book. There is a huge effort behind that book and I think Fred deserve our support. You can get it at No Starch Press.
I must admit that I am cheating a bit, because I read ahead quite a few chapters before rereading an earlier chapter and then commenting on that chapter on this site. This is to discover what is important to remember and / or easy to forget as I dig deeper into the content.
Each article has a list of references that I stongly suggest you have a look at, including the Erlang Language Reference, the ETS reference and Joe Armostrong's book Programming Erlang.
Pattern Matching (Again)
Pattern matching is much easier to achieve in Erlang than in many other languages, but the syntax takes some time to get used to. Instead of if-else or similar conditional branching, in Erlang you simply use functional declarations with a pattern.
A functional declaration consists of functional clauses separated by semicolons and it is ended by a period.
The pattern in the expression can be used for more than just conditional branching. It can be used to find stuff, for instance in a list:
findhead([H|_]) -> H. findsecond([_,S|_]) -> S.
Let's see it in practice. If we create a list containing atoms, [dolphins, krikkiters, oglaroonians, poghrils], the first expression should give us dolphins and the second krikkiters. We create a module finding_stuff and include the functions above:
-module(finding_stuff). -compile(export_all). findhead([H|_]) -> H. findsecond([_,S|_]) -> S.
Compiling and running the code:
If we want to compare two elements, we can use the following module:
-module(comparisons). -compile(export_all). same(X,X) -> true; same(_,_) -> false.
Running the code gives us:
It is not easy to see what is going on here. How does the expression same(X,X) really work? To understand that, we need to have a look at bound and unbound variables in Erlang.
Bound and Unbound Invariable Variables
When a variable is bound, it has a value connected to it. When a variable is unbound, no value is connected to the variable. Once bound, the value of the variable cannot be changed. The variable is immutable.
In the expression same(X,X), Erlang treats the second variable as already bound. Since both are named X, it compares the first unbound variable to the second bound variable, and will only return true when both are equal, because X cannot change value. Only if the "new" value is equal to the old one will there be a match, and the function returns true. If not, it passes on to the next pattern match, which is a catch-all, and returns false.
Still not clear? Check out Armstrong-s explanation of single assignment variables (synonym for immutable variables) in Programming Erlang, 2nd Ed., p.28.
There are limits to what you can do with pattern matching. Patterns aren't good at expressing ranges of values or types of data. This can be done with guards. A guard is simply an addition to the pattern matching, a boolean function placed after the key word, "when" and before the arrow, "->".
time_left(Time) when Time < 3 -> "So long and thanks for all the fish".
Calling time_left(X) with X less than 3 will give the response "So long and thanks for all the fish". You can also add statements to the guard with comma (,), semicolon (;), andalso and orelse. For instance:
time_left(Time,Race) when Time < 3, is_atom(Race) -> "So long and thanks for all the fish".
What are the differences between the operators?
- Comma, semicolon: Catch exceptions as they happen. Cannot be nested inside guards.
- andalso, orelse: Do not catch exceptions as they happen. Can be nested inside guards.
- Learn You Some Erlang - Syntax in Functions (Chapters in the paper version of the book: 3.Syntax in Functions, p.43)
- Programming Erlang, 4 - Modules and Functions
- Erlang Reference Manual, Expressions/li>
Other Erlang Resources
Erldocs - An alternative to the official sites.
Erlang Patterns - A collection of Erlang patterns
Rebar3 - A build tool for Erlang that makes it easy to compile and test Erlang applications and releases.
Erlang mailing lists and forums
The Google group Erlang Programming
Erlang on Stack Exchange
Erlang on Freenode - Use #Erlang
Erlang on Slack
"Learn You Some Erlang For Great Good", by Fred Hebert
"Programming Erlang", by Joe Armstrong
The Zen of Erlang, by Fred Hebert. A partly practical, partly philosophical take on Erlang.
Ericsson's coding standard for Erlang - Programming rules and conventions.
Getting started with Erlang using IntelliJ IDEA (including Rebar3).