There is one long term request - to have strict type checking in Elixir.
As far as I remember, @bcardarella mentioned that in the talk at Lonestar ElixirConf 2019. And after reading the How to make Dialyzer more strict? it’s clear that dialyzer is good to have and at the same time it’s not enough to check all possible function calls automatically. And eventually, there is no “one-button” tool to verify the data consistency throughout the app at the compile-time.
We have no Haskel like type inference with dialyzer types, that seems required to do that. And according to @josevalim, it seems impossible to introduce a native type system into the Elixir language itself without breaking compatibility with existing applications code.
There are several projects for building a compile-time type-safe BEAM language from the ground-up like Alpaca, Gleam, and some others Statically typed languages on BEAM. Same time, integration with other BEAM running software can be an issue according to @rvirding.
There are Elixir libraries f.e. ExType by @gyson, and proof of concepts like TypedElixir and MLElixir by @OvermindDL1. These seem are for making the strict equivalent of the dialyzer tool.
It looks like a not too optimistic situation.
It’d be good to look at the problem from a different angle. For what we need strict types when writing an application?
Usually, to check the consistency of states of business data, we operate in the app.
To model the data, we make structures, take out values from them, process them, and assemble back.
Here is an idea - we can match the value type with defined field type each time we assemble a struct at run-time, and raise or return an error on mismatch. And to differentiate the same typed values by the business meaning, we can wrap them into tagged tuples. That is, to keep the link of the separated value to the concrete struct with a tuple’s tag.
With means of the Domo library, that I’m glad to present, it’s expressed like the following.
defmodule Order do
use Domo
alias Measure.{Kilograms, Units}
deftag Id, for_type: String.t()
deftag Quantity, for_type: Kilograms.t() | Units.t()
deftag Note, for_type: :none | String.t()
typedstruct do
field :id, Id.t()
field :quantity, Quantity.t()
field :note, Note.t(), default: Note --- :none
end
end
Order.new!(
id: Id --- "156",
quantity: Quantity --- Kilograms --- 2.5
note: Note --- "Deliver on Tue"
)
The dialyzer can check the correctness of data assembly in the context of struct-value relation at the compile-time. At the run-time, the autogenerated new!/3, put!/3, and merge!/3 functions of the struct automatically matches values themselves against field type.
In combination with the expat library by @vic , the processing of the tags can be neat. Domo is fully compatible with the existing Elixir code bases and can be introduced in existing projects gracefully. More examples are in the Domo library’s documentation (the master version of Elixir is necessary to run). There is an example app included in the library’s repo.
Folks, is it an absurd idea to ask you to give feedback on the approach presented, and how it can be made even better?