Polyglot Survival Guide: Erlang, Haskell, F#, and Elixir
elixir erlang f# functional haskellLately I have been bouncing back and forth between Erlang and Haskell, two profoundly significant functional languages that I find both very different and very similar. They are very different in their philosophies and thought patterns, particularly around data types, but both being non-LISPy functional languages, they have enough similarities that I sometimes having trouble keeping them straight when I am coding. So I have started a simple list here cataloging some of their differences side by side. I imagine I will continue to add to it over time. Please comment to let me know what other features or constructs you might like to see added here.
I have now added entries for F# and Elixir, which I am also working with lately. While Elixir is the least adopted so far, I put it right after Erlang, since their relationship leads to a lot of similarities in the details cataloged here.
Functional Purity
- Erlang
- no
- Elixir
- no
- Haskell
- yes (in that all side effects are relegated to the IO monad)
- F#
- no
REPL
- Erlang
-
erl
- Elixir
-
iex
- Haskell
-
ghci
- F#
-
fsi
Exiting the REPL
- Erlang
-
q().
- Elixir
-
ctrl-c ctrl-c
orctrl-g q
- Haskell
-
:q
- F#
-
#quit;;
Line terminators
- Erlang
- English-like (, ; .)
- Elixir
- none
- Haskell
- none
- F#
- none
Typing
- Erlang
- dynamic
- Elixir
- dynamic
- Haskell
- static with inference
- F#
- static with inference
I/O
- Erlang
- io, file, and some other modules
- Elixir
- IO, File, and some other modules
- Haskell
- IO monad
- F#
- .NET Framework
Printing
- Erlang
-
io:format("Hello~n").
- Elixir
-
IO.puts "Hello\n"
- Haskell
-
putStrLn "Hello"
- F#
-
printfn "Hello"
Variable names
- Erlang
-
PascalCase
- Elixir
-
snake_case
- Haskell
-
camelCase
- F#
-
camelCase
Atoms
- Erlang
-
atom
- Elixir
-
:atom
- Haskell
- (none)
- F#
- (none)
Laziness
- Erlang
- hackable with funs
- Elixir
- streams lazy, lists not
- Haskell
- by default
- F#
- sequences lazy, lists not; explicit with lazy keyword
Infinite lists
- Erlang
- hackable with funs
- Elixir
- streams, yes; lists, no
- Haskell
- absolutely
- F#
- sequences with yield
Currying
- Erlang
- explicit with lambdas, no dedicated syntax
- Elixir
- explicit with lambdas, no dedicated syntax
- Haskell
- implicit
- F#
- implicit
Side effects
- Erlang
- yes
- Elixir
- yes
- Haskell
- never, except via the IO monad
- F#
- yes
Tuples
- Erlang
-
{"foo", "bar"}
- Elixir
-
{"foo", "bar"}
- Haskell
-
("foo", "bar")
- F#
-
"foo", "bar"
List construction/matching
- Erlang
-
[Head|Tail]
- Elixir
-
[h|t]
- Haskell
-
x:xs
- F#
-
x :: xs
Heterogeneous lists
- Erlang
- yes
- Elixir
- yes
- Haskell
- no
- F#
- no
Function calls
- Erlang
-
add(1,2)
- Elixir
-
add(1,2)
oradd 1,2
- Haskell
-
add 1 2
- F#
-
add 1 2
Lambdas
- Erlang
-
fun(X,Y) -> X + Y end
- Elixir
-
fn x,y -> x + y end
or&(&1 + &2)
- Haskell
-
\x y -> x + y
- F#
-
fun x y -> x + y
List comprehensions
- Erlang
-
[X || X <- [1,2,3,4,5], X > 3]
- Elixir
-
for x <- [1,2,3,4,5], x > 3, do: x
- Haskell
-
[x | x <- [1,2,3,4,5], x > 3]
- F#
-
{ for x in 1 .. 5 when x > 3 -> x }
Converting integer to string
- Erlang
-
integer_to_list(123)
- Elixir
-
to_string 123
- Haskell
-
show 123
- F#
-
string 123
Comments
- Erlang
-
% comment after percent
- Elixir
-
# comment after pound sign
- Haskell
-
-- comment after double hyphen
- F#
-
// comment after double slash
Mapping
- Erlang
-
lists:map(fun(X) -> X*X end, [1,2,3]).
- Elixir
-
Enum.map [1,2,3], &(&1 * &1)
- Haskell
-
map (x -> x*x) [1..3]
- F#
-
List.map (fun x -> x*x) [1 .. 3]