Typing in Elixir
Alex Rozumii, Toptal
Elixir Club 6, Kyiv
What is this talk about?
Basic data types
Arithmetic
iex> 1 + 1
2
iex> 1 + 1.5
2.5
iex> 1.5 + 1.5
3.0
Atoms
iex> :hello
:hello
iex> :hello == :world
false
iex> is_integer(:one)
false
iex> is_boolean(true)
true
Boolean
iex> true && false
false
iex> true || false
true
iex> is_atom(true)
true
iex> false == :false
true
iex> true == :false
false
String/Binary
iex> name = "リック"
"リック"
iex> "Greetings, #{name}"
"Greetings, リック"
iex> byte_size name
9
iex> String.length name
3
Lists / Char lists
iex> [:Kyiv, "Elixir", 'Club']
[:Kyiv, "Elixir", 'Club']
iex> [75, 121, 105, 118]
'Kyiv'
iex> 'Kyiv' == "Kyiv"
false
iex> ['E', 432123534]
['E', 432123534]
iex> [1 | []]
[1]
Tuples
iex> {1,2,3}
{1, 2, 3}
iex> File.read "/etc/ntp.conf"
{:ok, "server time.apple.com.\n"}
iex> elem({:ok, 321}, 1)
321
iex> tuple_size {:ok, "hello"}
2
Anonymous functions
iex> add = fn a, b -> a + b end
#Function<12.71889879/2 in :erl_eval.expr/5>
iex> add.(1, 2)
3
iex> is_function(add)
true
Not so basic built in data types
Keyword lists / Maps
Maps vs Keyword lists
iex> keyword_list = [{:user, "Homer"}, {:message, "I want donuts"}]
[user: "Homer", message: "I want donuts"]
iex> Map.new keyword_list
%{message: "I want donuts", user: "Homer"}
iex> List.first keyword_list
{:user, "Homer"}
iex> map = Map.new keyword_list
%{message: "I want donuts", user: "Homer"}
iex> map[:user]
"Homer"
Date / Time / DateTime
iex> Date.utc_today
~D[2017-03-10]
iex> Time.utc_now
~T[12:07:23.037483]
iex> DateTime.utc_now
%DateTime{calendar: Calendar.ISO, day: 10, hour: 12,
microsecond: {146776, 6}, minute: 7, month: 3,
second: 31, std_offset: 0, time_zone: "Etc/UTC",
utc_offset: 0, year: 2017, zone_abbr: "UTC"}
Regex
iex> Regex.split(~r/\s+/, "/pizzabot --again order great")
["/pizzabot", "--again", "order", "great"]
Range
iex> 1..10 |> Enum.each(&{IO.puts &1})
1
2
3
...
Thank you for listening.
Any questions?
Structs (e.g. Custom types)
Bare maps underneath
Bare maps underneath
iex> :maps.keys(1..100000)
[:__struct__, :first, :last]
iex> Map.keys(1..100000)
[:__struct__, :first, :last]
iex> (1..10).__struct__
Range
iex> Map.delete (1..1000000000), :__struct__
%{first: 1, last: 1000000000}
iex> Map.from_struct(1..1000)
%{first: 1, last: 1000}
defstruct
defmodule ChatCommand do
defstruct user: nil, message: ""
end
iex> %ChatCommand{}
%ChatCommand{message: "", user: nil}
iex> %ChatCommand{user: "Batman"}
%ChatCommand{message: "", user: "Batman"}
iex> %ChatCommand{user: "Batman", message: "I love black!"}
%ChatCommand{message: "I love black!", user: "Batman"}
OOP principles
OOP principles
- Inheritance
OOP principles
- Inheritance ☑
OOP principles
- Inheritance ☑
- Encapsulation
OOP principles
- Inheritance ☑
- Encapsulation ☑
OOP principles
- Inheritance ☑
- Encapsulation ☑
- Polymorphism
OOP principles
- Inheritance ☑
- Encapsulation ☑
- Polymorphism ...
Protocols
Protocols
defprotocol Bot.Sendable do
def to_message(data)
end
defimpl Bot.Sendable, for: ChatCommand do
def to_message(data) do
"<#{data.user}> #{data.message}"
end
end
msg = %ChatCommand{user: "Batman", message: "I love black!"}
iex> Bot.Sendable.to_message msg
"<Batman> I love black!"
iex> Bot.Sendable.to_message "ADSF"
** (Protocol.UndefinedError) protocol Bot.Sendable
not implemented for "ADSF"
iex:109: Bot.Sendable.impl_for!/1
iex:110: Bot.Sendable.to_message/1
Standard protocols
iex> to_string msg
** (Protocol.UndefinedError) protocol String.Chars not
implemented for %ChatCommand
(elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir) lib/string/chars.ex:17: String.Chars.to_string/1
defimpl String.Chars, for: ChatCommand do
def to_string(data) do
data.message
end
end
iex> to_string msg
"I love black!"
Specifications
Specifications
defmodule ChatCommand do
@type t :: %ChatCommand{user: String.t, text: String.t}
defstruct user: nil, message: ""
end
> elixir chatbot.ex
** (CompileError) chatbot.ex:2: undefined field text on struct ChatCommand
(elixir) lib/kernel/typespec.ex:1012: Kernel.Typespec.compile_error/2
(stdlib) lists.erl:1338: :lists.foreach/2
(elixir) lib/kernel/typespec.ex:811: Kernel.Typespec.typespec/3
(elixir) lib/kernel/typespec.ex:400: Kernel.Typespec.translate_type/3
chatbot.ex:1: (file)
Specifications
defmodule Messenger do
@spec repeat(integer, %ChatCommand{user: String.t, message: String.t})
def repeat(times, msg) do
end
end
> elixir chatbot.ex
** (CompileError) chatbot.ex:27: type specification missing return
type: repeat(integer, %ChatCommand{user: String.t(), message: String.t()})
(elixir) lib/kernel/typespec.ex:1012: Kernel.Typespec.compile_error/2
(elixir) lib/code.ex:370: Code.require_file/2
Specifications
defmodule Messenger do
@spec repeat(%ChatCommand{user: String.t, message: String.t}) :: String.t
def repeat(times, msg) do
end
end
> elixir chatbot.ex
** (CompileError) chatbot.ex:27: spec for undefined function repeat/1
(stdlib) lists.erl:1338: :lists.foreach/2
(elixir) lib/code.ex:370: Code.require_file/2
Specifications
defmodule Messenger do
@spec repeat(integer, %ChatCommand{user: String.t,
message: String.t}) :: String.t
def repeat(times, msg) do
end
end
dialyzer
Behaviours
Behaviours
defmodule Plug.Session.Store do
@moduledoc """
Specification for session stores.
"""
@type sid :: term | nil
@type cookie :: binary
@type session :: map
@callback init(Plug.opts) :: Plug.opts
@callback get(Plug.Conn.t, cookie, Plug.opts) :: {sid, session}
@callback put(Plug.Conn.t, sid, any, Plug.opts) :: cookie
@callback delete(Plug.Conn.t, sid, Plug.opts) :: :ok
end
Behaviours
defmodule Plug.Session.COOKIE do
@behaviour Plug.Session.Store
def init(opts) do
encryption_salt = opts[:encryption_salt]
....
%{key_opts: key_opts,
serializer: serializer,
log: log}
end
def get(conn, cookie, opts) do
%{key_opts: key_opts, log: log, serializer: serializer} = opts
...
end
def put(conn, _sid, term, opts) do
%{serializer: serializer, key_opts: key_opts} = opts
....
end
def delete(_conn, _sid, _opts) do
:ok
end
end
Time for questions
Thank you for listening
Typing in Elixir
By Alex Rozumii
Typing in Elixir
- 1,748