Phoenix, type annotations and Dialyzer

Elixir is a great language, but its lack of static types is a major drawback for me.

Dialyzer seems like a good way to fix this issue, but it seems like Phoenix doesn’t provide type annotations (at least that’s what I gather from reading the source code on GitHub), which makes type-checking essentially useless (it doesn’t matter if my code has types, if my framework doesn’t).

Is there any reason the Phoenix framework lacks types? Would resolving the issue be as easy as adding type annotations to everything? If so I would love to help out.

Dialyzer and @spec are, I believe, largely deemed to be mostly useful for code documentation purposes. Using type_check or norm should be better for your original goal – although no true compile-time static type checking in this ecosystem as you noted.

1 Like

While still early in development, you might be interested to hear about Gleam. A statically typed language for the BEAM.

That’s not really the case. Dialyzer is far away from what other static type systems provide. Elixir will stay an dynamically typed language even if you’re using dialyzer.

That’s not the case as well. Dialyzer doesn’t need explicit typespecs at all – to the contrary. Dialyzer does infer types everywhere. It’s more of a sanity check in dialyzer to compare the inferred types with what was supplied by typespecs and error if there’s a mismatch detected.

3 Likes

I believe that one of the main creators/maintainers of Phoenix (and the LiveView man himself) @chrismccord has stated previously that he’s not a huge fan of typespecs and thinks that they don’t provide too much benefit (feel free to correct me if I’m wrong).

I assume that neither he nor any of the other core maintainers want to maintain the typespecs and that in order to get them into Phoenix you’d not only have to help provide them but help maintain them as Phoenix changes.

This is not exactly true. Dialyzer will fail when you add a level of indirection: eg (from the little elixir otp book) :

def add(a,b) do 
   a + b 
end 

def inc(b) do
  add(1, :foo)
end
   

will trip dialyzer

But

def add(a,b) do 
   a + amount(b)
end 

def amount(x), do: x

def inc(b) do
  add(1, :foo)
end
   

Will not (ok, maybe it will, this is of the top of my head but the point stands). And then you can help it with specs.

The thing is, dialyzer is NOT a static type checker, it is (imho a very useful) static analysis tool, that does success type checking and some type inference.

As I’ve said elsewhere, it shouldn’t preclude writing typespecs as they are really useful for your colleagues and your future self (I can remember what my get_some_stuff() function does but I’m very bad at remembering whether it returns {:ok, stuff} or just stuff and raises if nothing comes up).

1 Like