elixirtypespec

What is the meaning of an Elixir typespec with types in parenthesis separated by the pipe operator?


I'm porting an Elixir library to Elm that uses type specs heavily but I'm having trouble finding documentation on some of the syntax used.

What is the following type expression expressing?

@type identifier :: (String.t | MyCustomTypeA.t | MyCustomTypeB.t)

Initially I modeled it as a tuple but now that I'm looking at it's usage it looks as though it may be a Discriminated Union. The problem with this assumption though is that I don't see any documentation for support of such things here (http://elixir-lang.github.io/getting-started/typespecs-and-behaviours.html).


Solution

  • You are correct in that it's a Discriminated Union. Neither the Elixir nor Erlang docs call it out directly but it can be inferred from the more in-depth docs (https://hexdocs.pm/elixir/typespecs.html)

    The only real call-out to this is the line

    All other types are built out of unions of predefined types.

    In your example, the parenthesis are not required. You could also write it as

    @type identifier :: String.t | MyCustomTypeA.t | MyCustomTypeB.t
    

    and it means that identifier can be either a String.t, a MyCustomTypeA.t, or a MyCustomTypeB.t

    Elixir inherits this from Erlang and it's well explained in Learn you some Erlang for great good

    (please remember that while Elixir builds on Erlang the syntax differs a bit)

    Erlang has union types, which allow you to describe a type that has two atoms in it, and built-in types, which are pre-defined types, not necessarily possible to build by hand, and they're generally useful.

    [...SNIP...]

    The notation to represent the union of types is the pipe (|). Basically, this lets us say that a given type TypeName is represented as the union of Type1 | Type2 | ... | TypeN. As such, the number() type, which includes integers and floating point values, could be represented as integer() | float(). A boolean value could be defined as 'true' | 'false'. It is also possible to define types where only one other type is used. Although it looks like a union type, it is in fact an alias.