Wiebe-Marten Wijnja
→ a set of possible values
→ Is value one of the allowed possibilities?
Thinking of types depends a lot on language
def myfun(a, b) do
# ... more code here
c = a - b # ???
# ... more code here
end
def myfun(a, b) do
# ... more code here
c = a - b # ???
# ... more code here
end
myfun(10, 20)
myfun(10, "abcd")
def myfun(a, b) do
# ... more code here
c = a - b # ???
# ... more code here
end
myfun(10, 20)
myfun(10, "abcd")
def myfun(a, b) do
# ... more code here
c = a - b # ???
# ... more code here
end
fn myfun(a: Int, b: Int) -> Int {
// ... more code here
c = a - b // <- Known to be fine
// ... more code here
}
myfun(10, 20)
myfun(10, "abcd")
def myfun(a, b) do
# ... more code here
c = a - b # ???
# ... more code here
end
fn myfun(a: Int, b: Int) -> Int {
// ... more code here
c = a - b // <- Known to be fine
// ... more code here
}
myfun(10, 20)
myfun(10, "abcd")
Find problems earlier...
... by being more explicit
It Depends
→ root cause
To Type or Not to Type? | Bogner & Merkel
@spec is_even(integer()) :: boolean()
Gradualizer: Promising, but experimental
Clarity
defmodule Foo do
def bar(a, b) do
# ...
end
end
defmodule Users.Highscore do
def top10(a, b) do
# ...
end
end
defmodule Users.Highscore do
def top10(users, scoring_fun) do
# ...
end
end
defmodule Users.Highscore do
@spec top10(list(User.t()), (User.t() -> float())) :: list(User.t())
def top10(users, scoring_fun) do
# ...
end
end
defmodule Users.Highscore do
@spec top10(list(User.t()), (User.t() -> float())) :: list(User.t())
def top10(users, scoring_fun)
when is_list(users) and is_function(scoring_fun, 1) do
# ...
end
end
defmodule Users.Highscore do
@spec top10(list(User.t()), (User.t() -> float())) :: list(User.t())
def top10(users, scoring_fun)
when is_function(scoring_fun, 1) do
ensure_list_of_users!(users)
# ...
end
defp ensure_list_of_users!(users) do
Enum.each(users, fn element ->
case element do
%User{} -> :ok
other -> raise("Error, #{inspect(other)} is not a user.")
end)
end
end
defmodule Users.Highscore do
@spec top10(list(User.t()), (User.t() -> float())) :: list(User.t())
def top10(users, scoring_fun)
when is_function(scoring_fun, 1) do
ensure_list_of_users!(users)
# ...
end
defp ensure_list_of_users!(users) do
Enum.each(users, fn element ->
case element do
%User{} -> :ok
other -> raise("Error, #{inspect(other)} is not a user.")
end)
end
end
defmodule Users.Highscore do
use TypeCheck
@spec! top10(list(User.t()), (User.t() -> float())) :: list(User.t())
def top10(users, scoring_fun) do
# ...
end
end
→ Generate checks from typespecs
→ Same typespec still usable for Dialyzer
→ Errors are standardized
→ Difficult checks also happen!
defmodule Users.Highscore do
use TypeCheck
@spec! top10(list(User.t()), (User.t() -> float())) :: list(User.t())
def top10(users, scoring_fun) do
# ...
end
end
defmodule Users.Highscore do
use TypeCheck
@spec top10(list(User.t()), (User.t() -> float())) :: list(User.t())
def top10(users, scoring_fun) do
# ...
end
defoverridable top10: 2
def top10(users, scoring_fun) do
TypeCheck.conforms!(users, list(User.t))
TypeCheck.conforms!(scoring_fun, (User.t() -> float()))
result = super(users, scoring_fun)
TypeCheck.conforms!(result, list(User.t()))
result
end
# Used for reflection & spectests
def __TypeCheck_spec_for_'top10/2'__ do
%TypeCheck.Builtin.Spec{
#...
}
end
end
NOTE: actual checks
Need more detailed types?
OTP 25: Type-based optimizations in the JIT
https://www.erlang.org/blog/type-based-optimizations-in-the-jit/
Manual use of
defstruct!
TypeCheck.conforms! / TypeCheck.conforms? / TypeCheck.conforms
Higher Confidence
defmodule Rating do
use TypeCheck
defstruct [:value, :author]
@type! t() :: %Rating{value: 1..5, author: String.t()}
@spec! average(list(t())) :: number()
def average(ratings) do
values = Enum.map(ratings, &(&1.value))
Enum.sum(values) / Enum.count(values)
end
end
defmodule RatingTest do
use ExUnit.Case, async: true
use TypeCheck.ExUnit
spectest Rating
# ... other tests :-)
end
If spectests are not enough,
write your own property-based tests
→ Types are usable as generators
Type definitions
Type-checking macros
Help greatly appreciated!
Out now!