Elixir is a dynamically typed language, so all types in Elixir are inferred by the runtime. Nonetheless, Elixir comes with typespecs, which are a notation used for declaring custom data types and declaring typed function signatures (specifications).
By default, Elixir provides some basic types, such as integer or pid, and also complex types: for example, the round function, which rounds a float to its nearest integer, takes a number as an argument (an integer or a float) and returns an integer. In the related documentation, the round typed signature is written as −
round(number) :: integer
The above description implies that the function on the left takes as argument what is specified in parenthesis and returns what is on the right of ::, i.e., Integer. Function specs are written with the @spec directive, placed right before the function definition. The round function can be written as −
@spec round(number) :: integer def round(number), do: # Function implementation ...
Typespecs support complex types as well, for example, if you want to return a list of integers, then you can use [Integer]
While Elixir provides a lot of useful inbuilt types, it is convenient to define custom types when appropriate. This can be done when defining modules through the @type directive. Let us consider an example to understand the same −
defmodule FunnyCalculator do @type number_with_joke :: {number, String.t} @spec add(number, number) :: number_with_joke def add(x, y), do: {x + y, "You need a calculator to do that?"} @spec multiply(number, number) :: number_with_joke def multiply(x, y), do: {x * y, "It is like addition on steroids."} end {result, comment} = FunnyCalculator.add(10, 20) IO.puts(result) IO.puts(comment)
When the above program is run, it produces the following result −
30 You need a calculator to do that?
NOTE − Custom types defined through @type are exported and available outside the module they are defined in. If you want to keep a custom type private, you can use the @typep directive instead of @type.