Elixir, a cool programming language:
Do you need to learn Erlang first to learn Elixir?
Nah, you got this. But if you already know Erlang, you might find this Elixir crash course for Erlang developers useful to get started. In either case, it’s important to know that Elixir programs can always, and sometimes have to, invoke functions from Erlang libraries. Elixir did not set out to rewrite the entire Erlang ecosystem; rather, it set out to modernize Erlang the language.
Jeff’s intro:
The Hello World program in Elixir is:
IO.puts "Hello, world"
You can run Elixir scripts on repl.it or TIO.
You can also install Elixir on your own machine. This will create several executables: (1) iex, an interactive shell, (2) elixir, to run scripts, (3) elixirc, to compile files, and (4) mix, the build tool.
Run your script now:
$ elixir hello.exs Hello, world
Filename extensions
Use the extension.exs
for scripts (files you intend to run as programs), and.ex
for modules (containing objects and services you export to other modules and scripts).
Now, check out this little script:
Enum.each 1..40, fn c -> Enum.each 1..c, fn b -> Enum.each 1..b, fn a -> if a * a + b * b == c * c do IO.puts "#{a}, #{b}, #{c}" end end end end
$ elixir triple.exs 3, 4, 5 6, 8, 10 5, 12, 13 9, 12, 15 8, 15, 17 12, 16, 20 15, 20, 25 7, 24, 25 10, 24, 26 20, 21, 29 18, 24, 30 16, 30, 34 21, 28, 35 12, 35, 37 15, 36, 39 24, 32, 40
Here’s on more script, this time with a command line argument:
defmodule Collatz do def steps(n, count \\ 0) do cond do n == 1 -> count rem(n, 2) == 0 -> steps div(n, 2), count + 1 true -> steps 3 * n + 1, count + 1 end end end if length(System.argv) != 1 do IO.puts "Exactly one command line argument required" else case System.argv |> hd |> Integer.parse do :error -> IO.puts "Integer command line required" {n, _} -> IO.puts Collatz.steps(n) end end
Neat, huh?
cond
for multiway conditionals, if
for simple conditionals, and case
for pattern matching.|>
operator is quite cool, right?Okay, time to go deeper.
Let’s illustrate a few things with IEx:
$ iex Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace] Interactive Elixir (1.11.2) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> 9 * (3 - 2) < 89 true iex(2)> 3 <= 5 and 8 > 13 or not true false iex(3)> "dog" <> "house" "doghouse" iex(4)> [h | t] = [1, 2, 3, 4] [1, 2, 3, 4] iex(5)> h 1 iex(6)> t [2, 3, 4] iex(7)> [x, y, z] = [10, 20, 30] [10, 20, 30] iex(8)> y * z 600 iex(9)> first..last = 2..89 2..89 iex(10)> last 89 iex(11)> colors = %{red: "rojo", green: "verde", blue: "azul"} %{blue: "azul", green: "verde", red: "rojo"} iex(12)> colors[:green] "verde" iex(13)> colors.green "verde"
These are the basic types (as far as I know, anyway, there may be more):
Type | Description | Examples |
---|---|---|
Integer | Whole number. Unbounded, yay. |
3 9853212977530319128888123123 0x3ffa 0b01101001
|
Float | Floating-point number. |
3.15 27.88E-200
|
Atom | A (named) symbol. Starts with a colon. |
:ok :blue :empty :true
|
List | A sequence of values stored as a linked list in memory. |
[1, :ok, "hello"] [] [[1, 2], [3, 4]]
|
Tuple | A sequence of values stored contiguously in memory. |
{1, :ok, "hello"} {} {:rectangle 30.0 22.95}
|
BitString | A sequence of bits stored contiguously in memory. |
<<7::3, 89::24, 1::8>> <<97, 98, 99>> "Hello 🙌🧇🦊🥑"
|
Map | Key-value pairs. Note the shorthand syntax when the keys are atoms. |
%{:a => 1, 2 => :b} %{"z" => true, 1 => 0, 0 => 1} %{:red => "rojo", :blue => "azul"} %{red: "rojo", blue: "azul"}
|
Function | A function. |
fn x -> not x end fn -> 0 end fn x, y -> 2 * x + y end fn f, x -> f.(f.(x)) end &"Good #{&1}, #{&2}" &(&1 * 2 + &2) &String.reverse/1
|
PID | Identifier for an Elixir process. The pid of the currently executing process is obtained by calling self/0 . |
self spawn(fn -> 0 end)
|
Port | A thing you use to communicate with external processes. |
Port.open({:spawn, "ls"}, [])
|
Reference | A unique identifier, generated on demand with make_ref/0 . | make_ref |
In Elixir, types are not objects, and in fact, you can’t cleanly “get” the type of an expression; instead, you have functions that return whether or not an expression has a type:
# Checks on the basic types true = is_integer 8919821982888885323298 false = is_integer 5.0 true = is_float 2.0 true = is_atom :ok true = is_atom :false true = is_atom false true = false == :false true = is_atom nil true = is_tuple {"Hi", 3, :true} true = is_list ["Hi", 3, :true] true = is_function(fn x -> x * x end) true = is_map %{x: 3, y: 5} true = is_bitstring <<7::3, 199, 8000::31>> true = is_bitstring <<420>> true = is_bitstring "Elixir is cool 😎💃🏽😍" true = is_pid self() true = is_reference make_ref() # Other checks (numnber, boolean, nil, binary) true = is_number(9.8) true = is_number(5) true = is_boolean(true) true = is_boolean(false) true = is_boolean(:false) true = is_nil(nil) false = is_binary <<7::3, 199, 8000::31>> true = is_binary <<420>> true = is_binary "Elixir is cool 😎💃🏽😍" IO.puts("Everything checks out!")
Things that are not exactly typesThese all have built-in test functions (
boolean
The atoms :true
and:false
. Elixir binds the namestrue
andfalse
to these, respectively, as a convenience.nil
The atom :nil
. Elixir binds the namenil
to this as a convenience.number
All the integers and floats. binary
Bitstrings in which the number of bits is divisible by 8. is_boolean
,is_nil
,is_number
, andis_binary
) functions, but the actual datatype for their values is something else.
Technically, there is a hack to get the type, though: if you are using IEx, you can use the inspect function i
to get a ton of useful information about a value, including its type!
iex(1)> i "Happy 🎃 Day, いずみ" Term "Happy 🎃 Day, いずみ" Data type BitString Byte size 25 Description This is a string: a UTF-8 encoded binary. It's printed surrounded by "double quotes" because all UTF-8 encoded code points in it are printable. Raw representation <<72, 97, 112, 112, 121, 32, 240, 159, 142, 131, 32, 68, 97, 121, 44, 32, 227, 129, 132, 227, 129, 154, 227, 129, 191>> Reference modules String, :binary Implemented protocols Collectable, IEx.Info, Inspect, List.Chars, String.Chars
i
function in IEx on a number of different values, with the intent about learning more about Elixir types. Try it on each of the examples in the table above.
Elixir’s integer type is unbounded, and the float type is probably IEEE binary-64, but the docs don’t seem to require this. In fact, Elixir does not seem to do Infinity and NaN like other languages!
is_float 8
Atoms are just atoms, or just symbols, they’re just what they are. So the atom :dog
is just the atom :dog
. It is not text. It is not a string. It is just :dog
.
The atoms :true
, :false
, and :nil
are so special you can write them false
, and nil
. The first two are called booleans but you already knew that. The atom nil
is used to represent that something is missing or unknown.
...
Most languages need to manipulate text. Do we need a string type? Not really, the reasons for it aren’t that great. In fact, strings and text are not the same. And in most languages that have string types, their string type is badly broken. Few languages, if any, get text right. Swift and Elixir do text pretty well.
In Elixir, a string is just a binary that is a valid UTF-8 sequence.
TODO
You make your own types with structs and protocols:
TODO
There are more types in the Elixir Standard Library (some of which you’ll notice have a neat syntax):
TODO
Functions come in two flavors: (1) named functions, defined with def
or defp
inside a module, and (2) anonymous functions of the form fn
params ->
expression (or a short form). You can call the former with or without parenthesizing the arguments; the latter requires a dot plus parentheses for its arguments. If there are no arguments, you just invoke the function via its name (or the variable it has been assigned to). To refer to the function without calling it, use the &
prefix.
Docs
Using Erlang Modules
Like what you see here? Want to learn more? Want to get really good at this cool language? Check out:
We’ve covered: