Option types are implemented in statically typed languages using dedicated types, such as Option in OCaml and Maybe in Haskell. The same approach can be used in Erlang:
-type option(T) :: {some, T} | none.
However, since Erlang allows union types to overlap, it might be advantageous to use a common atom for representing absence throughout the codebase:
-type option(T) :: {some, T} | null.
%% Only used at third-party boundaries; immediately converted to `option(T)`.
-type nullable(T) :: T | null. % T is never going to be `null`.
-type json_value() :: % Specification omitted.
-type json() ::
{some, json_value()} |
null | % Explicit JSON null.
undefined. % Missing input field or omitted output field.
Would you consider this idiomatic Erlang, or do you see any drawbacks to this design?