As mentioned in my previous post, I recently have gotten very interested in Functional Programming (FP), and have really enjoyed the new learning curve. So, a dozen stack overflow questions later and a day of Haskell tutorials, I decided to give Erlang a try. Another SO question lead me to thinking that the Euler problems are a great way to get acquainted in FP. They weren’t wrong and it has been a largely enjoyable experience. None of the questions are too difficult, but they make for great example problems to understand how differently you might solve these challenges in different languages. Below are some of the things that I found interesting along the way, and some of the aspects of Erlang I am enjoying thinking critically about.
As with most languages, a great place to start is to understand the basic data types. Erlang has a relatively limited type system and it makes getting up an running relatively easy. Atoms, Numbers, Characters, Lists, Tuples and Maps can get you very far in the Erlang world, and it has some convenient BIF’s to convert easily between them. All Erlang variables are immutable.
There is also the ability to create more advanced types, by building upon the Record type. This would be something more akin to a struct. For the most part, however, I find it is more useful to use these structures as a last resort, and see how far the basic types can take you.
There are no typical for or while loops in Erlang. And, if you’re anything like me, at first that makes things tricky. But then you start to learn how everything is just another case of recursion. And soon enough it will become your best friend. Here is an example that may help get you through the basics.
Now, before we get too caught up in syntax and pattern matching, and all this good stuff. Let’s just make sure we understand that all we are really doing is looping through this loop_to_100 function that will print the count until N reaches 100. This is akin to something like a while loop where N < 100, starting at N = 0. Now, this is ONE way to do things in Erlang, but I’d argue, not a great one. The best way would be to start with your data, and iterate through it. But before, we get to a better example, we need to address the types of recursion.
Once you understand this code sample, you can do some things in Erlang. This brings the best out of Erlang’s syntax, types, recursion, and pattern matching. Since, we have already discussed a few of these features already, I am going to focus on Pattern Matching. My short, carefully crafted example above actually has 3 distinct examples of pattern matching and we are going to step through each one. First,
greet_group() -> ;
In Erlang world, here we have a single function greet_group/1, and will execute differently based on the parameters passed to the function. For example
Our function is pattern matching the arguments supplied, and executing the appropriate function body. Of course, the first pattern matching an empty list, and the second one matching a non-empty list. Now, this part might make sense to you, but you still might be scratching your head, and I can assure you that I was to. If that is the case, the key to understanding the previous outputs is probably with this part
I mean, I just mentioned that the second block of greet_group/1 was matching a non-empty array. What? Well, the answer is this is another (and very common/important) way to pattern match in Erlang. In fact, this is actually the pop operation for a list. The following code is probably easier to understand:
[Head|Tail] = [1,2,3]. % Head = 1, Tail = [2,3]
Hopefully that explains the execution flow of our greet_group/1 function. The function will keep popping from the array until the function matches an empty array and simply return an empty array. Hopefully its all coming together for you, but I promised you three examples of pattern matching and that is only two. The last example is the most classic example that you might find across many other languages in a switch or let statement. But lets go over it just in case:
From this we can glean that if Next is a tuple with a first value of atom friend, and only a second value, then we will say hello to the textual representation of the Name. Notice, I say textual representation because we have no assurances that Name is actually a String (5, or friend, or [1,2,3]) would also match. The last part of this is the _Else control syntax, meaning that this will match all remaining cases.
It would be a grave disservice to you readers if I did not bring up the best part of Erlang. That would be Concurrency. Now, I have less experience here than grants me right to discuss it, but I would like to emphasize it anyway. You can find an example of simple concurrency here. And if you would like some input to see how it works, find it here
But I am sure you did not come here for simple concurrency. So finally, we can get to something cool. Some straightforward, distributed, synchronized programming, using only the power of Erlang (ok and docker). But here it is, and in case you would like to follow along at home.
You will need to open 2 terminal tabs. In the first window (one line at a time)
docker run --rm -it --name server1 -h server1 --net my-network -v "$(PWD):/code" erlang /bin/bash
erl -sname server1 -setcookie fib
and in the 2nd (again one line at a time)
docker run --rm -it --name server2 -h server2 --net my-network -v "$(PWD):/code" erlang /bin/bash erl -sname server2 -setcookie fib cd(code). c(server2). server2:wait_for_fib_of_duration(5).
You can see the output below
Hopefully, you thought this was as cool as I did and enjoyed a little intro to Erlang. Now, go ahead and have some fun for yourself.