With the announcement of 1.19 rc0 and the path to user-supplied type annotations, I want to make the case for inline types—both for function parameters and local variables—rather than the currently implied direction of out-of-band annotation signatures using a $
prefix. The examples shared so far suggest something like:
$ integer() -> integer()
def inc(i), do: i + 1
This pattern – separating types from function heads – is unusual and, in my opinion, a step backwards compared to almost every modern language that supports types.
Why Inline Type Annotations Matter
Clarity and Locality
Types belong next to the values they describe. Splitting types from the function definition forces developers to mentally reconcile two separate lines:
# Inline (clear and self-contained)
def inc(i: integer): integer do
i + 1
end
# Out-of-band (disconnected)
$ integer() -> integer()
def inc(i), do: i + 1
Inline syntax offers immediacy—everything needed to understand the function is in one place.
Developer Familiarity
Languages like TypeScript, Swift, Kotlin, Rust, C#, and even Python (via PEP 484) all use inline typing for a reason. It’s readable, discoverable, and familiar to modern developers. A $
line above each function feels more like a callback to the days of @spec
, and misses the opportunity to modernize.
Better Tooling & UX
Inline types play better with IDEs and editors. They improve autocomplete, refactoring support, inlay hints, and inline docs. Detached signatures complicate tooling and reduce feedback during development.
Variable Type Annotations Are Just as Important
It’s not just function signatures. We need a way to annotate local variables inline:
x: integer = some_potentially_dynamic_value()
case y: String.t() <- some_api_call() do
...
end
Other languages with gradual typing (Python, TypeScript, Julia) allow inline variable annotations. Without this, dynamic code within functions remains opaque to tools and reviewers, limiting the power of gradual typing.
Obviously retrofitting types on elixir is complex, and everyone appreciates the care being taken by José and the core team. But inline types—both for function signatures and variables—are essential if Elixir wants to deliver a type system that feels natural, modern, and ergonomic.
Would love to hear thoughts from others, and to also hear from the core team why it appears that $
annotations have so much momentum already!