But sometimes we do want to capture that error information! In this case, we use ~case~!
#+BEGIN_SRC elixir
case Sequences.fib(-1) do
{:ok, val} -> val
{:error, err} ->
IO.puts("Ran into :error #{inspect(err)}")
0
end
#+END_SRC
** Piping
Elixir's pipe operator ~|>~ allows programmers to easily write statements as a composition of functions. It simply takes the value of the
function on the left, and passes it as the first argument to the function on the right.
For example, to find the length of the longest string in a list of strings:
#+BEGIN_SRC elixir
["Hello, world", "Another string", "Where are all these strings coming from"]
|> Enum.map(&String.length/1)
|> Enum.max()
#+END_SRC
** Meta-programming
Akin to my favorite language of all time, LISP, Elixir provides a way to interact directly with code as data (and thus the AST) via a powerful macro system.
However, they are not as elegant, and for that reason, Chris McCord suggests in his book "Metaprogramming Elixir":
#+BEGIN_QUOTE
Rule 1 : Don't Write Macros
#+END_QUOTE
The main reasoning is that it becomes difficult to debug, and hides too much from the user. These are fine trade-offs when you're working alone.
*** when-prime the functional way
#+BEGIN_SRC elixir
defmodule Prime do
def is_prime(2), do: true
def is_prime(n) when rem(n, 2) == 0 or n <= 1, do: false
def is_prime(n) do
is_prime_helper(n, 3)
end
defp is_prime_helper(n, i) when i * i > n, do: true
defp is_prime_helper(n, i) when rem(n, i) == 0, do: false
defp is_prime_helper(n, i) do
is_prime_helper(n, i + 2)
end
end
#+END_SRC
#+BEGIN_SRC elixir
when_prime_do = fn n, when_true, when_false ->
if Prime.is_prime(n) do
when_true.()
else
when_false.()
end
end
when_prime_do.(10, fn -> "10 is prime" end, fn -> "10 is not prime" end)
#+END_SRC
*** when-prime the metaprogramming way
#+BEGIN_SRC elixir
defmodule When do
defmacro prime(n, do: true_body, else: false_body) do
quote do
if Prime.is_prime(unquote(n)), do: unquote(true_body), else: unquote(false_body)
end
end
end
require When
When.prime 10, do: "10 is prime", else: "10 is not prime"
#+END_SRC
*** Real-world use-case: ~use~
One such use case for macros (besides those covered previously in my LISP presentation) is to emulate module "inheritance" to share functions.
We can think of a module in Elixir as a set of functions. Then, we can perform unions of modules by the ~use~ macros.
Additionally, with ~behaviours~ we can define callbacks to implement in each unioned module.
** Erlang SSH Module - (maybe) building a tic tac toe game!
So much networking stuff is built on top of Erlang that its standard library - OTP - has implementations for tons of stuff you'd regularly reach for a library to help; ssh, snmp,
ftp, are all built in "OTP Applications".
It requires a little bit of time with headaches, but the docs are generally pretty good (with occasional source code browsing): [[https://www.erlang.org/doc/man/ssh.html]]