Elixir

<3 
just a taste
defmodule Greeter do
  @moduledoc """
  All the Greetings
  """
  
  @doc """
  Says hi to someone

  ## Examples
    iex> Greeter.hello("Joe")
    "hi Joe"
  """
  def hello(name) do
    "hi #{name}"
  end
end
defmodule Greeter.Person do
  defstruct [:name]
end

Functions

+ DaTA

Pattern
Matching

defmodule Greeter do
  
  # Pattern matching on input
  def hi(%Person{name: name}) do
    hi(name)
  end

  def hi(%Person{name: nil}) do
    raise "Invalid Name"
  end
  
  def hi(name) do
    "hi #{name}"
  end
end
iex(1)> person = %Greeter.Person{name: "Mike"}
%Person{name: "Mike"}

iex(2)> Greeter.hello(person)
"hi Mike"


iex(3)> Greeter.hello("Joe")
"hi Joe"

iex(4)> Greeter.hello(%Person{})
** (RuntimeError) Invalid Name

Processes & Messages &
Concurrency

# Create a Pool of Greeters which run concurrently
iex(1)> pool = [Greeter.start(), Greeter.start()]
[#PID<0.312.0>, #PID<0.313.0>]

# Map over the pool of Greeter Servers, say hello
iex(2)> Enum.map(pool, fn(svr) -> 
          Greeter.hi(svr, "Robert") 
        end)
[{:greet, #PID<0.84.0>, "Robert"}, 
 {:greet, #PID<0.84.0>, "Robert"}]

# Ask the REPL to print any messages it has received
iex(3)> flush()
"hi Robert from #PID<0.312.0>"
"hi Robert from #PID<0.313.0>"
:ok
defmodule Greeter do
  @doc "spawn a greeter server process"
  def start() do
    # spawn is a built in function
    # which takes a function and runs
    # it in a new process
    spawn(&loop/0)
  end

  @doc "ask server to say hello to `name`"
  def hi(server, name) when is_pid(server) do
    # get the calling process id
    from = self()
    # send a message to the "server" process
    send(server, {:greet, from, name})
  end

  @doc "loop to receive messages"
  def loop() do
    # blocks waiting for messages
    receive do
      {:greet, from, name} ->
        # send reply from self
        me = inspect(self())
        msg = "hi #{name} from #{me}"
        send(from, msg)
    end
    loop() # get next message
  end
end

distributed elixir

iex> Node.connect(:"bar@computer-name")
true

iex> Node.list()
[:"bar@computer-name"]

iex> Node.spawn_link(:"bar@computer-name", fn ->
...>  receive do
...>    {:ping, client} -> send client, :pong
...>  end
...> end)

iex> pid = Node.spawn_link :"foo@computer-name", fn ->
...>   receive do
...>     {:ping, client} -> send client, :pong
...>   end
...> end
#PID<9014.59.0>

iex> send pid, {:ping, self()}
{:ping, #PID<0.73.0>}

iex> flush()
:pong
:ok
$ iex --sname bar
# Nothing else to do, 
# as code from :"foo@." 
# will run on this machine
$ iex --sname foo

Abstractions over processes

task = Task.async(fn -> do_some_work() end)
foo = do_some_other_work()
res = Task.await(task)

Task (Async & AWAIT)

# On the remote node
Task.Supervisor.start_link(name: MyApp.DistSupervisor)

# On the client
Task.Supervisor.async({MyApp.DistSupervisor, :remote@local},
                      MyMod, :my_fun, [arg1, arg2, arg3])

Distributed Tasks across machines

Hygienic Metaprogramming

defmodule AssertionTest do
  # Notice we pass "async: true", this runs the test case
  # concurrently with other test cases. The individual tests
  # within each test case are still run serially.
  use ExUnit.Case, async: true

  # Use the "test" macro instead of "def" for clarity.
  test "the truth" do
    assert true
  end
end
# Imports only from/2 of Ecto.Query
import Ecto.Query, only: [from: 2]

# Create a query
query = from u in "users",
        where: u.age > 18,
        select: u.name

# Send the query to the repository
Repo.all(query)

processes are actors
No Shared Memory
supervision + Crash isolation
EZ multi-node data-parallelism

defmodule HelloPhoenixApi.Router do
  use HelloPhoenixApi.Web, :router

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", HelloPhoenixApi do
    pipe_through :api

    get "/", HelloController, :index
  end
end
defmodule HelloPhoenixApi.HelloController do
  use HelloPhoenixApi.Web, :controller

  def index(conn, _params) do
    json(conn, %{hello: "world"})
  end
end
describe "GET /hello" do
  test "should return world", %{conn: conn} do
    json =
      conn
      |> get(hello_path(conn, :index))
      |> json_response(200)

    assert %{"hello" => "world"}
  end
end
defmodule NetworkLed.Blinker do
  use GenServer
  alias Nerves.Leds

  #...

  def enable() do
    GenServer.cast(__MODULE__, :enable)
  end

  def handle_cast(:enable, state) do
    Logger.info("Enabling LED")
    Leds.set(green_led: true)

    {:noreply, state}
  end
end
defmodule NetworkLed.Http do
  use Plug.Router

  plug(:match)
  plug(:dispatch)

  get "/", do 
    send_resp(conn, 200, 
      "Feel free to use API endpoints!")
  end

  get "/enable" do
    NetworkLed.Blinker.enable()
    send_resp(conn, 200, "LED enabled")
  end

  match _, do: send_resp(conn, 404, "Oops!")
end

elixirschool.com

config :nerves_leds, names: [green_led: "led0"]
defmodule BlogWeb.Schema do
  use Absinthe.Schema
  import_types BlogWeb.Schema.ContentTypes

  alias BlogWeb.Resolvers

  query do

    @desc "Get all posts"
    field :posts, list_of(:post) do
      resolve &Resolvers.Content.list_posts/3
    end

  end
end
defmodule BlogWeb.Schema.ContentTypes do
  use Absinthe.Schema.Notation

  object :post do
    field :id, :id
    field :title, :string
    field :body, :string
  end
end
defmodule BlogWeb.Resolvers.Content do
  def list_posts(_parent, _args, _resolution) do
    {:ok, Blog.Content.list_posts()}
  end
end
<3 
thanks


belfastelixir.org
meetup.com/Belfast-Elixir
meetup.com/BelfastFP
 

elixir-JTT

By Steven Holdsworth

elixir-JTT

A fast and furious tour of all things elixir

  • 311