Geovane Fedrecheski - geovane@lsi.usp.br
School of Engineering
University of São Paulo
f(g(x), h(...))
Steve Vinoski, IEEE Computing Society 2009
"... ironically, the resurgence of interest in FP languages owes a lot to Java’s popularity."
"Another reason [...] is the move toward multicore architectures."
"... developers look for the opportunity to
get more done with less."
Lisp
Elm
Core committer
Unsatisfied with thread-based concurrency
~2010
Unsatisfied with thread-based concurrency
Repetitive strain
injury
by Bruce A. Tate
context
Created in the Telecommunication industry
...
~1980
requirements
Programming language
Virtual Machine - BEAM
Framework - OTP
-module(fact).
-export([fac/1]).
fac(0) -> 1;
fac(N) -> N * fac(N-1).
The VM
Concurreny
Fault tolerance
Data structures
Consistent libs
Modern tooling
I loved everything that Erlang had
but I hated everything that it didn't had
- José Valim
Goals
> {method, path} = {:get, "temp"}
{:get, "temp"}
> method
:get
> path
"temp"
b = :binary.encode_unsigned(258)
# b has value <<1, 2>>
<<x, 2>> = b
# x has value 1
i = :binary.decode_unsigned(<<x, 2>>)
# i has value 258
request = {:get, "temp"}
case request do
{:post, path, payload} ->
send_resp 200
{:get, path} ->
send_resp 200, "{'value':17.5, 'unit': 'celsius'}"
end
defmodule Math do
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n-1) + fib(n-2)
end
Math.fib(8) # => 21
> add = fn(a, b) -> a + b end
#Function<12.71889879/2 in :erl_eval.expr/5>
> add.(1, 2)
3
> Enum.map([17.9, 20.2, 18.4], fn(x) -> x * 2 end)
[35.8, 40.4, 36.8]
Can be passed as parameters
spawn
A lightweight thread of execution
(The key Erlang/Elixir abstraction)
message
Excuse me, have you ever heard of (VM) processes?
Scheduler
Sched.
Sched.
Sched.
OS Processes
OS Threads
VM Processes
OS
pid = spawn(fn ->
:timer.sleep(3000)
IO.puts "hi"
end)
fn ..
spawn
self()
sleep
puts
A lightweight thread of execution
pid = spawn(...)
send(pid, "hello")
receive do
msg ->
IO.puts msg
end
fn ..
spawn
self()
receive
send
Node.spawn(
:"app@computer-2",
fn -> Hello.world end)
send
receive
app@computer-1
app@computer-2
spawn
spawn
worker
worker
worker
worker
import Supervisor.Spec children = [ worker(Cache, []), supervisor(DatabaseSupervisor, []), supervisor(TCPSupervisor, [4040]) ] Supervisor.start_link(children, strategy: :one_for_one)
app@computer-1
app@computer-2
Constrained Application Protocol
kind of a "smaller HTTP, for IoT"
request
response
(binary header)
0.01 (GET) (Request)
0.02 (POST) (Request)
2.05 (Content) (Response)
4.04 (Not Found) (Response)
000.00001
000.00010
010.00101
100.00100
1
0
0
0.01
123
01000000 00000001 0000000001111011 11111111
255
64 1 0 123 255
(empty)
(empty)
(empty)
%Message(type: :con,
code: :get,
msg_id: 123,
options: [])
int coap_parseHeader(coap_header_t *hdr,
const uint8_t *buf,
size_t buflen)
{
if (buflen < 4)
return COAP_ERR_HEADER_TOO_SHORT;
hdr->ver = (buf[0] & 0xC0) >> 6;
if (hdr->ver != 1)
return COAP_ERR_VERSION_NOT_1;
hdr->t = (buf[0] & 0x30) >> 4;
hdr->tkl = buf[0] & 0x0F;
hdr->code = buf[1];
hdr->id[0] = buf[2];
hdr->id[1] = buf[3];
return 0;
}
https://github.com/1248/microcoap/blob/master/coap.c
def decode(<<1::2, type::2, token_len::4,
code_class::3, code_detail::5,
msg_id::16,
token::binary-size(token_len),
rest::binary>>) do
def decode(<<1::2, type::2, token_len::4,
code_class::3, code_detail::5,
msg_id::16,
token::binary-size(token_len),
rest::binary>>) do
code = decode_code(code_class, code_detail)
token = decode_token(token, token_len)
{options, payload} =
decode_options_and_payload(rest)
%Message{
version: 1,
type: Registry.from(:types, type),
code: code,
msg_id: msg_id, token: token,
options: options, payload: payload
}
end
int coap_parseHeader(coap_header_t *hdr,
const uint8_t *buf,
size_t buflen)
{
if (buflen < 4)
return COAP_ERR_HEADER_TOO_SHORT;
hdr->ver = (buf[0] & 0xC0) >> 6;
if (hdr->ver != 1)
return COAP_ERR_VERSION_NOT_1;
hdr->t = (buf[0] & 0x30) >> 4;
hdr->tkl = buf[0] & 0x0F;
hdr->code = buf[1];
hdr->id[0] = buf[2];
hdr->id[1] = buf[3];
return 0;
}
$ mix new temperature_sensor --sup
$ cd temperature_sensor
$ mix test
$ iex -S mix
def read_service_description do
Path.join("#{:code.priv_dir(:temperature_sensor)}",
"service_description.jsonld")
|> File.read!()
end
$ mix test
$ iex -S mix
# mix.exs
defp deps do
[{:poison, "~> 3.0"}]
end
def read_service_description do
Path.join("#{:code.priv_dir(:temperature_sensor)}",
"service_description.jsonld")
|> File.read!()
|> Poison.decode!()
end
$ mix deps.get
$ mix test
def register(sd) do
Task.start_link(fn ->
BrokerHTTPClient.register(sd)
end)
end
# mix.exs
defp deps do
[
{:poison, "~> 3.0"},
{:broker_http_client,
git: "git@git.febrace.org.br:swarm-unit/broker-http-client-ex.git"}
]
end
$ mix deps.get
$ mix test
Plug.Adapters.Cowboy.child_spec(:http,
TemperatureSensor.Router,
[], [port: sd_port])
# mix.exs
defp deps do
[
{:cowboy, "~> 1.0.0"},
{:plug, "~> 1.0"},
{:poison, "~> 3.0"},
{:broker_http_client,
git: "git@git.febrace.org.br:swarm-unit/broker-http-client-ex.git"}
]
end
$ mix deps.get
$ iex -S mix
defmodule RouterTest
$ mix test
match _ do
Logger.warn "Invalid request at #{conn.request_path}"
send_resp(conn, 404, "EEEPA")
end
$ mix test
$ iex -S mix
plug BrokerHTTPClient, :authorize
$ iex -S mix
$ curl \
-H "x-m2m-origin: alice"
http://localhost:8001/temperature-sensor/value
:observer.start
(this is the appendix!)