Elixir for Swift Developers

I took a glance at Erlang a couple years ago but never got far enough to understand the syntax. It seemed very different and strange.

If that describes you too, you should definitely take a look at Elixir.

Here's a recommendation: do a couple short tutorials in Elixir, then find an Erlang tutorial but follow it in Elixir. You'll learn Elixir better because you have to translate from Erlang to Elixir, and you'll learn something about Erlang along the way as well. After you know a bit about Elixir, you'll find that reading Erlang code mostly makes sense.

Look at it the same way as Objective-C and Swift: sure, you can get by knowing Swift and not Objective-C, but if you work in the ecosystem you're eventually going to need to either interoperate with write some Objective-C, so it's really best to have a good grasp of both.

Part of the reason Elixir clicked for me is that I became acquainted with more robust pattern matching in Swift. But Elixir takes it to another level.

Swift's pattern matching possibilities become interesting as we can switch on multiple things and cast values in each case:

let thingOne = true 
let thingTwo = true

func evaluate() -> Bool {
  switch (thingOne, thingTwo) {
    case (true, true):
      print("They're both true!")
      return true
    case (_, _):
      print("We got something unexpected.")
      return false
  }
}

It looks really similar in Elixir:

thingOne = true
thingTwo = true

def evaluate() do
  case {thingOne, thingTwo} do
    {true, true} ->
      Logger.info "They're both true!"
      :ok
    other ->
      Logger.info "We got something unexpected."
      :error
  end
end

But in Elixir, pattern matching isn't constrained to the function body. We can move the match up into the function declaration:

def evaluate({true, true}) do
  Logger.info "They're both true!"
  :ok
end

def evaluate(other) do
  Logger.info "We got something unexpected."
  :error
end

We can also match parameter arguments against other parameter arguments. The first evaluate declaration blow will be called if the two values captured in the flag variable match:

def evaluate({flag, _unused}, flag) do
  Logger.info "flag is #{flag}"
end

def evaluate(_tuple, _flag) do
  Logger.info "The 'flag' match failed."
end

evaluate({true, false}, true) # "flag is true"
evaluate({true, false}, false) # "The 'flag' match failed."

There's even more here - but the point I want to make is that Swift and Elixir are just similar enough that learning one will help you ease into the other.

In Swift we're familiar with force-unwrapping an optional value:

var optionalValue: Int?
let result = optionalValue!

If optionalValue is nil as it is above, the unwrap will fail, and this will be a runtime error that will crash the app. Since it represents a latent crasher, force-unwrapping is dangerous and is usually discouraged.

Elixir is diferent though. Consider this line:

:ok = :gen_tcp.send(socket, data)

Nothing here looks dangerous at first. But that :ok value on the left hand side is a static symbol, not a variable that can be assigned to. This reveals something interesting about the = operator in Elixir -- it's actually a match operator, not an assignment operator. If the function :gen_tcp.send/2 returns anything besides :ok, the match will fail and the process will crash. In this sense it's similar to force-unwrapping, but with very different philosophy:

"Let it crash" is a sound strategy in Elixir. This does not mean "let it crash and then give up", as you might see by just dropping a fatalError() in Swift 😬. It means "let it crash because the supervisor architecture will bring it back up again immediately and it will probably work next time".

It's very interesting to read about this philosophy of Elixir/Erlang - you'll come away with ideas and principles that you can apply to many contexts in order to build more fault-tolerant systems.

posted on 04/22/2017

tags: Elixir

Comments:

rss

blog powered by Pelican