12 January 2015

fun() -> functional end.

There's something special about having obscure, dust-covered tidbits of knowledge.  Like a collection of rare books (though much easier to move) or an exotic flower garden (though requiring less water), collections of the mind tend to bring much joy and diversion.  The same principle, it seems, must apply to programming languages.  After years of working with more traditional languages like C / C++ and Fortran, and then delving into the exotic but almost useless ones like Forth and m68k assembly, practical circumstances finally forced me to learn a programming language that is slightly less confined to particular niches (when did C get that way?)... perhaps even a bit, dare I say, bourgeois.  Going too mainstream, of course, would be unthinkable, so I settled on Erlang.  That was more than six months ago now, and I haven't looked back.  The project, by the way, was an laptop encryption status reporting tool for Linux, and a corresponding web application for viewing the information.  There are plenty of them out there for Windows and Mac OS X, but none for our favorite FOSS platform.

What's so special about Erlang, you might ask?  Well, here are the highlights...

  • It relies heavily on the functional paradigm (more on that later)
  • There's built-in concurrency and message passing - no extra libraries needed
  • Typically, Erlang is compiled to byte-code, but can also be interpreted
  • It runs in a VM (comparing this to Java would be a disservice, let's avoid that)
  • The code of an module can be changed while the module is running, provided that a few basic rules are observed.
  • It's notoriously difficult to break and/or compromise, again provided that a few basic rules are observed
What is it particularly good at?  It's great for moving data from point 'A' to point 'B' based on intelligent, reliable, and rich decision-making.  It was originally created in the mid-80s to power telephone network switches.  Today, it shines as an integral component in products like ejabberd, WhatsApp, Facebook Messenger, Call of Duty, and Chef, to name just a few.

What is it particularly bad at?  Erlang is flat-out terrible at advanced manipulation of binary data.  Things like image and video processing, emulators, heuristic file scanning are out of its purview.

So, it's useful and has some big names associated with it, but is unknown to the general populace.  Perfect!  But where should one start?  The Erlang web site is fairly helpful, and will get you going in terms of getting the programming environment up and working (if you're on just about any GNU/Linux distro, it's probably already available for download from the normal software repo).  But that doesn't address what to do with the language.  If you like the modular, discrete, "learn it one small step at a time" approach, then exercism is for you.  In my case, I prefer the "sink or swim" approach, so after using the online version of Learn You Some Erlang for great good! as a primer, I started writing a simple demo point-of-sale system.  I wanted menus, tickets, and receipts.  And that's when the proverbial sh*t hit the fan...



Part of Erlang's magic is called referential transparency.  This means that any given expression (an action taken) could be replaced with its value (what it 'returns') without changing the way that the program works.  When referential transparency is maintained, Erlang is deterministic.  This is what allows hot code swapping to work, and also contributes to the system's message passing power.

...but if you're coming from imperative programming, it's your worst nightmare!

Take a simple 'for loop' in C:
for ( x = 0; x < a; x++ ) { printf( "Doing something...\n", x ); }
The concept is simple: you want to do something a variable number of times.  But because the value of x changes as the loop runs, and x could be used before or after the loop (try setting it as, e.g. a void and watch the hilarity!) this breaks referential transparency.  For this reason, in Erlang, 'variables' (which aren't actually variable) cannot have their value changed once defined, in any particular context.

Instead, to accomplish this same goal, you have to back up on your chain of reasoning, and ask what it is that you're trying to accomplish.  To just do the exact same thing as above, preserving referential transparency, in Erlang you might instead say:
myMessage( a ) -> io:write( string:copies( "Doing something...\n", a ) ).
In this example, one would simply call myMessage (a function) with the number of times a as the first argument.  This preserves referential transparency, i.e. any instance of myMessage( N ) could be replaced with the output value (which in this case would be the exit value of the io:write function, the atom ok).

Most probably, however, if you're using a 'for loop' in C, you're trying to do something with an array.  If you back up far enough on your chain of reasoning in Erlang, most such actions can be very quickly represented as list comprehensions, which (as the name suggests) build one list by processing one or more others in some way.  Say you want to convert a string, which is just syntactic sugar for a list and works the same way in Erlang, to uppercase... but remove all spaces.
[ string:to_upper( char ) || char <- "just a simple test", char /= 32 ]
In this case, you (the programmer) don't need to know how long the list is, or walk through it one item at a time- just tell Erlang how to process it, and let it go!

Now we've covered referential transparency, the pros and cons of Erlang, and how to go about getting started in it.   Soon I'll talk about the message passing and concurrency parts, and eventually get into anonymous function callbacks, one of my favorite Erlang tricks.

No comments:

Post a Comment

Your comments are welcome. Please keep them professional, courteous, and respectful of the blog author and of other commentors.