Автор: Давыденков Михаил
К сожалению не все языки и фреймворки соответствуют современным требованиям
Иногда бывает так:
Или так:
Ставим зависимости
$ sudo aptitude install -y \
build-essential \
libncurses5-dev \
openssl \
libssl-dev \
fop \
xsltproc \
unixodbc-dev \
systemtap-sdt-dev \
autoconf
To enable GUI Toolkit:
$ sudo aptitude install -y \
libwxbase2.8 \
libwxgtk2.8-dev \
libqt4-opengl-dev
Устанавливаем менеджер версий эрланга - KERL
curl -O https://raw.githubusercontent.com/yrashk/kerl/master/kerl
chmod a+x kerl && sudo mv /usr/bin
KERL_CONFIGURE_OPTIONS=" \
--with-dynamic-trace=dtrace \
--enable-dynamic-ssl-lib \
--enable-dirty-schedulers \
--enable-smp-support \
--enable-sctp \
--enable-threads \
--enable-kernel-poll \
--enable-shared-zlib \
--disable-silent-rules \
--disable-debug \
--enable-hipe \
--enable-native-libs \
" \
kerl build 18.2.1 18.2.1
kerl install 18.2.1 18.2.1
Устанавливаем менеджер версий эликсира - KIEX
\curl -sSL https://raw.githubusercontent.com/taylor/kiex/master/install | bash -s
kiex install 1.2.3
kiex default 1.2.3
Добавляем в .bash_profile или .zprofile
export PATH=$PATH:$HOME/.kerl/18.2.1/bin
export PATH=$PATH:$HOME/.kiex/bin
source "$HOME/.kiex/elixirs/elixir-1.2.3.env"
должно работать:
mix new project_name
Далее ставим феникс и можно работать
mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
должно работать:
mix phoenix.new project_name
Главная фишка эликсира - это BEAM, виртуальная машина эрланга. Посмотрим чем может удивить эрланг!
Простейшие функции
hello_func = fn ->
IO.puts "hello"
end
Порождение процесса
spawn(hello_func)
Порождение процесса, ожидающего сообщениe
hello_func = fn ->
IO.puts "hello"
receive do
:goodbye ->
IO.puts "goodbye"
_ ->
IO.puts "unknown message"
end
end
spawn(hello_func)
Процесс завершается с exit(normal) и второй раз не отреагирует на наше сообщение
Функция в модуле (c хвостовой рекурсией)
defmodule Hello do
def hello_func do
IO.puts "hello"
receive do
:goodbye ->
IO.puts "goodbye"
hello_func
_ ->
IO.puts "unknown message"
hello_func
end
end
end
pid = spawn(Hello, :hello_func, [])
send(pid, :goodbye)
send(pid, :foobar)
Функция в модуле передающая стейт между вызовами (c хвостовой рекурсией)
defmodule Hello do
def hello_func(counter) do
IO.puts "hello"
receive do
:goodbye ->
IO.puts "You've said goodbye #{counter} times"
new_counter = counter + 1
hello_func(new_counter)
_ ->
IO.puts "unknown message"
hello_func(counter)
end
end
end
pid = spawn(Hello, :hello_func, [])
send(pid, :goodbye)
send(pid, :foobar)
Интроспекция в эрланге / эликсире
:observer.start
А вообще тема серьёзная и требует изучения (есть разные интсрументы как в эрланге, так и в эликсире)
Составление пифагоровых троек через генераторы списков (List Comprehensions)
defmodule Triple do
def pythagorean(n) when n > 0 do
for a <- 1..n,
b <- 1..n,
c <- 1..n,
a + b + c <= n,
a*a + b*b == c*c,
do: {a, b, c}
end
end
Triple.pythagorean(48)
LC используется в фениксовых темплейтах для работы с коллекциями
Удобности для асинхронного кода (промисы)
task = Task.async(fn -> 1 + 2 + 3 end)
res = 4 + 5
res + Task.await(task)
Распределённый вариант
task = Task.Supervisor.async {CustomSupervisor, :"foo@computer-name"}, fn ->
{:ok, node()}
end
Пример скрипта на async await
defmodule WebsitePipeline do
# def map_titles(sites) do
# sites
# |> Enum.map(fn(url) ->
# url |> get_body |> extract_title
# end)
# end
def map_titles(sites) do
sites
|> Enum.map(fn(url) ->
Task.async(fn -> url |> get_body |> extract_title end)
end)
|> Enum.map(&Task.await/1)
end
defp get_body(url) do
IO.inspect url
HTTPotion.get(url).body
end
defp extract_title(html) do
title_pattern = ~r"([^<]*) "
Regex.run(title_pattern, html) |> Enum.at(1)
end
end
defmodule WebsitePipelineTest do
use ExUnit.Case
test "Mapping a pipeline of websites" do
sites = ["http://example.org",
"http://slashdot.org",
"http://elixir-lang.org",
"http://www.erlang.org"]
expected_titles = ["Example Domain",
"Slashdot: News for nerds, stuff that matters",
"Elixir",
"Erlang Programming Language"]
assert expected_titles == WebsitePipeline.map_titles(sites)
end
end
Управление in-memory состоянием (простая абстракция вокруг стейта процесса - Agent)
{:ok, agent} = Agent.start_link fn -> [] end
Agent.update(agent, fn list -> ["eggs"|list] end)
Agent.get(agent, fn list -> list end)
Agent.stop(agent)
Умеет get_and_update за одну операцию
Управление in-memory состоянием (GenServers)
Если что-то посложнее и похитрее, то правильнее использовать GenServerЛюбимая программа Джо Армстронга
# shell 1
iex --name "foo@192.168.1.69"
:erlang.set_cookie(node(), :foobar)
:erlang.register(:shell, self())
returned_value = receive do
{:become, some_fn} ->
some_fn.()
end
#shell 2
iex --name "bar@192.168.1.60"
erlang.set_cookie(node(), :foobar)
echo_server = fn ->
receive do
{from, value} ->
send(from, value)
end
end
foo = {:shell, :"foo@192.168.1.69"}
send(foo, {:become, echo_server})
send(foo, {self, "hey there"})
flush()
Протоколы - вариант реализации полиморфизма в эликсире
defprotocol Odd do
@doc "Returns true if data is considered odd"
def odd?(data)
end
defimpl Odd, for: Integer do
require Integer
def odd?(data) do
data
|> Integer.is_odd
end
end
defimpl Odd, for: Float do
def odd?(data) do
Odd.odd?(round(Float.floor(data)))
end
end
defimpl Odd, for: List do
def odd?(data) do
Odd.odd?(Enum.count(data))
end
end
Odd.odd?(2.1)
Odd.odd?([1])
Odd.odd?(%Animal{})
Активно используется в phoenix для сериализации-десериализации
Если необходимо можно использовать эрланговские библиотеки без особых проблем
Пример простого сервиса c использованием пула воркеров на эрланге (poolboy)А ещё в elixir довольно хорошие возможности по метапрограммированию, созданию макросов и написанию DSL
Можно почитать в оффициальных докахРассмотрим на реальном примере - Phoenix-trello
Другой пример - Hex Web, API сервер для сайта package менеджера Hex.pm