Background
Following the advice of a book I have been recently reading Grokking Functional Programming, I have come across the concept of Types.
The book defends that native types are something to be avoided, and that we should use custom types. Long story short, the authors defend this eliminates a whole class of bugs in on itself.
Code
So to test this idea, with the help of wonderful people from this community, I came up with a macro I call NewType
:
defmodule NewType do
defmacro deftype(name, type) do
quote do
defmodule unquote(name) do
@opaque t :: {unquote(name), unquote(type)}
@spec new(value :: unquote(type)) :: t
def new(value), do: {unquote(name), value}
@spec extract(new_type :: t) :: unquote(type)
def extract({unquote(name), value}), do: value
end
end
end
@spec is_type?(data :: {atom, any}, new_type :: atom) :: boolean
def is_type?({type, _data}, type), do: true
def is_type?(_data, _new_type), do: false
end
How to use it?
First you define a type. Let’s say, name.ex
:
defmodule Type do
import NewType
deftype(Name, String.t())
end
And then you use it!
def run_3 do
print(Name.new("dow"))
end
Problem
I have made it so my code works with dialyzer perfectly.
defmodule Test do
alias Type.Name
@spec print(Name.t()) :: binary
def print(name), do: Name.extract(name)
def run_1 do
# dialyzer complains !
Name.new(1)
end
def run_2 do
# dialyzer complains !
print("john")
end
@spec run_3 :: binary
def run_3 do
print(Name.new("dow"))
end
end
However, these are more code samples than tests. These “tests” do not fail. I know my code behaves as expected because I see dialyzer complaining in my IDE, but if/when I make changes to this, I will want to run it automatically and not open my IDE and look at it (or run mix dialyzer
and looks at the output ).
Question
Is there a way to have a test that ensures dialyzer complains and have it run on mix test
?