elixir-kiss
Usage
mix deps.get
# terminal 1
iex --name a@127.0.0.1 -S mix
# terminal 2
iex --name b@127.0.0.1 -S mix
# either terminal, Node.list. example:
iex(b@127.0.0.1)1> Node.list
[:"a@127.0.0.1"]
Posted on September 25, 2019
Yet another programming language. There are some unique characteristics of elixir that I have not experienced with other languages. First, the way I approach learning a new language might be of interest. What I do is start a repo for the language or framework, in this case elixir-kiss
(keep it simple stupid).
mix deps.get
# terminal 1
iex --name a@127.0.0.1 -S mix
# terminal 2
iex --name b@127.0.0.1 -S mix
# either terminal, Node.list. example:
iex(b@127.0.0.1)1> Node.list
[:"a@127.0.0.1"]
From there I start exploring basic ideas that I'm curious about and make branches off the original test bed.
The main thing you'll find elixir being used for is some kind of distributed system. It's a fun idea to think of a cluster of computers as a single application. To get an application "networked" together, you can do this in a few lines. Here is one example using a library that clusters 2 nodes.
defmodule MyApp.App do
use Application
def start(_type, _args) do
topologies = [
example: [
strategy: Cluster.Strategy.Epmd,
config: [hosts: [:"a@127.0.0.1", :"b@127.0.0.1"]],
]
]
children = [
{Cluster.Supervisor, [topologies, [name: MyApp.ClusterSupervisor]]},
# ..other children..
]
Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
end
end
There are a couple ways you could cluster your applications. The above example uses a builtin strategy called Erlang Port Mapper Daemon. Things like DNS, Multicast UDP gossip, or other API based methods can be used.
There are a number of unique attributes of elixir. Elixir's control flow is heavily based on pattern matching. This means you define functions based on the data structure being passed in. That looks something like:
post "/login" do
{:ok, body, conn} = read_body(conn)
case Poison.decode(body) do
{:ok, %{"email" => "", "password" => ""}} ->
send_resp(conn, 400, Poison.encode!(%{errors: %{email: "Please provide an email.", password: "Please provide a password"}}))
{:ok, %{"email" => email, "password" => password}} ->
send_resp(conn, 200, "")
{:error, :invalid, 0} ->
Logger.info("No body provided for /login")
send_resp(conn, 400, Poison.encode!(%{errors: %{email: "Please provide an email.", password: "Please provide a password"}}))
{:ok, %{"email" => email}} ->
Logger.info("No password provided for /login")
send_resp(conn, 400, Poison.encode!(%{errors: %{password: "Please provide a password"}, email: email}))
{:ok, %{"password" => _}} ->
Logger.info("No email provided for /login")
send_resp(conn, 400, Poison.encode!(%{errors: %{email: "Please provide an email."}}))
end
end
In elixir there are no return or break statements. The last line in a function is the return statement. This one really caught me off guard. Return statements are definitely designed for procedural mentality. Not having returns really emphasizes the pattern matching functionality.
Documentation is a first class citizen. I've never seen this before.
defmodule Math do
@moduledoc """
Provides math-related functions.
## Examples
iex> Math.sum(1, 2)
3
"""
@doc """
Calculates the sum of two numbers.
"""
def sum(a, b), do: a + b
end
If you configure your tests to use doctests, it will actually assert against the iex>
statements. Very cool.
Running elixir can be pretty complex due to it's distributed nature. But elixir is using erlang under the hood which happens to come with some handy GUI tools built in. You can see some of that here.
"Let it crash". Elixir – and Erlang – encourage you to code the happy path. Runtime errors should be allowed to crash the process. 1
Lastly, elixir has a Read Eval Print Loop
(REPL) runtime ability that makes it easy to test thoughts and explore the running application.
Even though elixir has reasonable types (tuples, maps, lists, etc.), doing type conversion I found to be confusing. If I wanted to cast something from a map into a user defined struct, it did not go smoothly.
I was not very pleased with the database driver. The most recommended interface is an abstracted library called Ecto. To use this interface you have to define repos, schemas, changesets or queries. The queries look like some kind of Domain Specific Language
(DSL) and changsets is a foreign concept to me. It's surprising that connections, prepared statements, and queries is not the suggested driver.
Some library documentation, although heavily documented, doesn't seem very helpful at times.
There isn't a language that is the silver bullet for all problems. Elixir is no different. It's still a young language, being originally created in only 2011, it's foundation is based on erlang which is much older. I may look back at it when I have a need for distribution or clustering.
Some interesting links:
Posted on September 25, 2019
Sign up to receive the latest update from our blog.