Elixir vs F# – opinionated syntax comparison

Note: There are some awesome comments to this post that add a lot of value. Please check them below.

This is the second part of my Elixir adventures and another post for “Get Noticed” competition.

I was planning to start furiously coding on my project for this second post and start building some web API with Phoenix. But Gutek suggested, that first I should really dig into some internals that will help me understand how Phoenix works – thanks for this advice. On top of that, I didn’t really have time to dig properly, but I managed to look a bit into syntax and I have mixed feelings.

I won’t be doing an introduction to Elixir post here. You can find a lot of resources on that, for example, Gutek’s series in Polish, official documentation or this short article. Instead, I’m gonna compare it to something that I’m familiar with –  F# syntax. And it’s nowhere near comprehensive comparison. Just a few things that I found interesting and worth noting.

Comparing stuff

Right from the start, there are few differences here. F# uses just ‘=’
to compare if two things are equal. Elixir has two comparison operators ‘==’ and ‘===’. First one is standard compare operator. Second, from my current understanding, is useful mostly for comparing if numbers are of the same type. To explain, look at this example:

// F#
1 = 2             // false
1 = 1             // true

1 = 1.0           // This yields compile error
float(1) = 1.0    // true

Although we didn’t declare any types in F#, it will infer them during compilation. And as a strongly typed language, will not allow comparing values of two different types.

# Elixir
1 == 2            # false
1 == 1            # true

1 == 1.0          # true
1 === 1.0         # false

Elixir is dynamically typed. It means, that it will also infer types, but this will happen in the runtime and also will do casts for you.

For not-equal F# uses ‘<>’ and Elixir ‘!=’ and ‘!==’. Generally, Elixir is here more consistent with most programming languages, so I’ll give it a point here, but I appreciate type safety of F# also. You can also notice that those languages use a different convention for comments.

In Elixir ‘=’ is also used for matching which is quite powerful.

Immutability

Although both languages are immutable by default, there are some differences in approach.

In Elixir, value is immutable so you cannot change it, but you can assign the “label” to some other value.

# Elixir
a = 1             # value "1" is now labelled "a"
a = a+1           # label "a" is changed: now "2" is labelled "a"
a = a*5           # value "10" is now labelled "a"

But if you want to refer to the current value of, i.e. when using match operator, you can do it this way:

# Elixir
b = 1
b = 2             # rebinding variable to 2
^b = 3            # matching: 2 = 3 -> error

First thing that came to my mind when saw it, were C language pointers :)

F# allows mutability, but it has to be openly declared, and then you need a different operator to change the value. Mutability is mostly allowed for compatibility reasons with .NET libraries, so you shouldn’t abuse it.

// F#
let a = 1         // binding value "1"" to label "a"
a = 2             // returns false (it is just comparing)
a <- 2            // compile error

let mutable b = 1 // binding value "1"" to mutable variable "b""
b <- 2            // changing value of variable "b""
b = 2             // returns true

In this part, F# is for me clear winner. You cannot change value bind to a label. It is much less confusing and makes more readable code.

List operations

List operations are generally very similar. What I found interesting in Elixir, you can match not only head and tail, like in F# but several first elements:
EDIT: As anonguy pointed out in the comments, that’s also possible in F#. Updated the code sample.

# Elixir
[ a, b, c | tail ]
// F#
head::tail
a::b::c::tail // that also works

There are two things worth mentioning while we’re on lists. Pipe operator (|>) works pretty much the same in both languages. In Elixir it binds the first parameter of the function, and in F# last one, but that’s the main difference. It’s a matter of convention and doesn’t really matter in the end. Just worth knowing.
EDIT: as Chris and Paul Blair pointed out, this has a tremendous impact on how currying and partial application works and makes F# much easier in that regard. Check out the comments for details.

The classic approach to lists is that you usually iterate through them with for loop. It’s possible in F#, but Elixir doesn’t have “for” loop. You have to do it in a more functional way, i.e. through recursion. For me, that’s a huge plus on Elixir side, because it forces you to use proper functional approach. In F# for loops are a gateway drug to imperative programming :).

Functions and modules

The first thing that I find annoying in Elixir is that every ‘def’ and ‘defp’ must be paired with ‘end’. It’s like curly braces all over again. Or Visual Basic. It makes code dirty and is excessive. In F#, blocks of code are delimited by the level of whitespace, similar to Python.

In Elixir, functions must be wrapped in Modules. It doesn’t create a big pain, but again – something I don’t have to do in F#. On the other hand, Elixir allows you to do multilevel Modules, which may be convenient in some situations.
EDIT: Anil Mujagic mentioned in the comments, that it also works in F#.

Pattern matching

A bit about Elixir pattern matching was mentioned in the first paragraph. “=” parameter has some impressive qualities. You can also pattern match on function parameters, like shown below in the second example. And you can further simplify it with guards.

# Elixir
# case statement
def blank?(value) do
    case value do
        nil    -> true
        false  -> true
        ""     -> true
        _other -> false
    end
end

# pattern matching on function parameters
def blank?(nil),    do: true
def blank?(false),  do: true
def blank?(""),     do: true
def blank?(_other), do: false

# pattern matching on function parameters with guards
def blank?(value) when value in [nil, false, ""], do: true
def blank?(_other), do: false

In F# it looks similar to the case statement in Elixir. You can also use guards with it and much more.

// F#
let x = 
    match 1 with 
    | 1 -> "a"
    | 2 -> "b"  
    | _ -> "z" 

I couldn’t recreate the same example easily, because of strong typing of F#.  The Same variable cannot have values of different types, and nulls are non-existent in this language. You could have something similar using discriminated unions.

I’m not a fan of Elixir’s approach to this problem with declaring several functions. I prefer F# way again.

Summary

As mentioned in the beginning, I have mixed feelings. For the last couple of years, I’ve been hearing a lot how beautiful Elixir is. And I can imagine for a lot of folks coming from other languages it is. But I’ve been spoiled with F# for last 5 years and I must admit, it’s still my number one. That being said, Elixir lands on the strong second position in terms of beauty. I do appreciate some big uncompromising design decisions that José made to make Elixir much more functional. F# has some “gateway drugs” to imperative programming, as they wanted to leave that option open too and be compatible with the rest of .NET. Big points for Elixir for that. There are some features of F# like discriminated unions or units of measures, that I haven’t found a good replacement in Elixir, but I’m also at the beginning of my journey. I also like F# more for strong typing.

Additional resources

F# has an abundance of operators. Some of them are really crazy. Check this Microsoft document to see all of them.

Quick guides on Elixir and F# syntax. The second one comes from the excellent blog of Scott Wlaschin. If you want to dive into F# more, I highly recommend it.

Next week I’ll be diving into internals. Hopefully, I will find time for that. Come back next week for more Elixir, and if you’re interested in Machine Learning, check my subjective drop of interesting articles in that area.

This post was edited to fix inaccuracies that were pointed out in the comments. Thank you for kind, constructive and informative comments!

18 thoughts on “Elixir vs F# – opinionated syntax comparison”

  1. Maybe you should compare the compilation errors between F# and Elixir+Dialyzer. Dialyzer is used so often, that using it can be seen as a standard build step – F# just has that step “built-in”.

  2. > On the other hand, Elixir allows you to do multilevel Modules, which may be convenient in some situations.

    If you’re talking about nested modules, that’s possible in F# too.

  3. You mention the difference in how the pipe-forward operator works, with Elixir replacing the first argument and F# replacing the last one. This hides something that is, I think, a bigger win for F#, namely, that to partially apply a function in F# you just omit the parameters starting on the right. With Elixir, partially applying functions is more involved. Because the signatures of functions in Elixir includes their arity, you can’t just pass them around without extra syntax (see José’s explanation here: http://stackoverflow.com/a/18023790). This can lead to gems like `1 |> (&(&1 * 2)).()` — and you have to remember those outer parentheses; `1 |> &(&1 * 2).()` won’t work.

    1. Wow, that’s a good point. I still haven’t wrapped my head around partial application in Elixir, but the example you brought up does look ugly ;). Thanks for the comment and a link.

    2. I was *just* about to mention this. I have to second this as a big win for F#. Currying and partial application are really awesome once you’re used to them.

    3. I was *just* about to say this. Currying + partial application are a big part of what makes F# awesome. I think it’d be worth hilighting in a language comparison. But over all, great writeup. Thanks, Michal!

  4. Hi Michał,

    thanks for taking the time to compare those two languages. In my opinion F# clearly wins in terms of clarity and conciseness. Also, glad to see there’s another FSharper in this edition of DajSiePoznac!

    Cheers,
    Youenn (www.ybouglouan.pl)

  5. 1 === 1.0 # true
    Are you sure about the above? For me it’s counter intuitive, usually triple-equals is stronger.

Leave a Reply

Your email address will not be published. Required fields are marked *