Stories of success or despair when foregoing typespecs in Elixir/Erlang

I was wondering if there is anyone here who either maintains or has any experiencing with a large Elixir/Erlang project in production that does not use typespecs.

I’m not looking for a general discussion on the merits of typing! I’m also not looking for any direct advice on whether I should use them or not (I get plenty of that as it is)—I’m just asking for any stories of those who have forgone their use, good or bad, specifically in Elixir/Erlang (I have plenty of experience with large, untyped projects in other languages).

Personally, I have used typed languages before (though never in production). I understand and have seen some of the benefits, and I’ve done a lot of reading on the subject. Yet, I still really enjoy (and mostly prefer) writing in dynamic languages. I was excited to learn that Elixir is dynamic only to realize the community is seemingly very pro typespec, and that’s totally cool!!! I’m just wondering if there is also faction that is having some untyped success …or some untyped failures.

Once again, I’m not looking for a debate here! I’m looking for stories and to get a feel for the community as I’m new here.

Also, maybe someone could point me toward an open source project that is untyped? I would be interested to see how the style might differ.

Thank you! :cowboy_hat_face:

4 Likes

Here’s an open source Elixir project with no typespecs: norm.

The author is a prominent member of the Elixir community who is pretty vocal about not being a fan of at least Dializer. So you might hunt around in his projects for other examples.

I want to say this much: types are optional in Elixir. Period. You are not wrong if you want to use Elixir without types. That’s what optional means. Program how you want to program.

10 Likes

I’ve been in several moderately big Elixir projects and honestly? You can be quite fine without. Enough defensive coding and good guards and runtime checks will net you even more benefits compared to typespecs. Elixir’s typespecs are, sadly, quite an anemic attempt at static type checking. It can catch a few mistakes here and there but its return of investment isn’t high.

As as @JEG2, do what you like. It really depends on the team and the project.

3 Likes

Thank you for making me aware of Chris Keathley. It’s cool to see some code and I’ve now watched a couple of his talks that I’ve enjoyed. Well, the one on raft is beyond where I’m currently at, but was still able to follow up until the end.

I generally do just do what I want, haha, but I’m also quite sensitive to community standards. I’m probably just bad at searching as whenever I have the word “type” in my search term, it makes for some very pro-typing discussions.

Maybe you meant that typespecs are optional in Elixir. Because otherwise I would strongly disagree. Functions will take and return a set of expected types, thinking about what they are is no more optional in elixir than in python, the fact that these languages don’t have a fancy static type system doesn’t change that.

As for typespecs, I would only say it’s a matter of being polite. The users of your public APIs will be happier if they don’t have to read your source code to find out what your functions expect and return. That being said this is entirely optional.

Also, dialyzer will work fine without typespecs, so the issue here is much less one of static analysis and/or catching errors than one of explicitness/code documentation.

4 Likes

I removed it from a moderately large code base and i had no regrets about doing that.

We kept typespecs as documentation where it felt appropriate, but removed dialyzer and it was nice.

5 Likes

I’m going to go out on a limb and assume that yes, @JEG2 did mean “typespecs” here! Obviously (well, ok, unfortunately this isn’t so obvious to everyone) we all have to put very intentional thought into types which is just as important in dynamic languages.

Thanks for your answer! I realize that my question was really being asked in the context of writing an application as opposed to a library. In a library I expect every public function to have extensive @documentation attached to it which can be read with h (though I always just look online anyway). But yes, good point!

Also, I had no idea that dialyzer and typespecs weren’t intertwined! TIL. I really need to properly look into it.

hrmmm, after having just finished my other reply and seeing your response, that’s more good info. Either way, I clearly need to look into Dialyzer and decide for myself.

I’m a huge fan of static typing (I’ve even made a statically typed Erlang based programming language), yet I’ve never managed to get any value out of Dialyzer.

For me it is too slow and not thorough enough to be used as a refactoring aid or design tool, so I would rather not use it at all.

I do sometimes still use typespecs for documentation, most frequently on the most important functions of the library I’m working on.

9 Likes

For applications I rarely use typespecs, and never Dialyzer. The value hasn’t been there and the CPU load doesn’t feel worth it.

On the other hand, I find typespecs invaluable for documentation and therefor use them for all of my libraries.

8 Likes

I’m not using typespecs in my application, but am using TypeCheck. This borrows heavily from typespec syntax, provides runtime checks, and supports custom checks using any Elixir code. It also compiles typespecs behind the scenes for tools like Dialyzer (which I don’t use). I was previously mostly content with just using pattern matching and guards for documentation and to enforce types, but appreciate the added bit of documentation and in your face error messages when the specs are out of date.

4 Likes

I strongly agree with this take.

You need to have some mechanism to capture the correctness of your functions and documenting the same. Types help. Tests do too. Norm does too. Credo. Module docs. Unit tests, Property based tests. Integration tests.

Here’s the thing. No one I know does ALL of those things. Find the mix that works for you and your team. A team from Java or Haskell will probably do better with type specs. A team from Ruby or Python might not.

Fit the tools to the team.

3 Likes

Yep, I come from Ruby and am a (generally) studious TDD’r. I certainly didn’t mean this question to come off as, “Has anyone had success dropping all safety nets/correctness validations?” I was very specifically talking about types and looking to get a feel for the community’s take on typing in a dynamic language.

All this to say that I agree with you :smiley:

You communicated perfectly. I was clumsy.

What I meant is that Elixir has many technologies that are checks and balances for similar problems, and taken in small enough doses, they can really help a project.

Types are one such check.

3 Likes

Author of the TypeCheck library here.

In my ~16 years of professional software engineering now (oof.) I have used both languages that allow very little restrictions of what values are passed around (JavaScript, Ruby) as well as languages that have a large number of tools to specify and restrict what values can be passed around (Rust, Haskell).

I feel myself very much agreeing with @redrapids. There are no silver bullets, but many tools that exist which each allow you in their own way to reduce the potential for certain kinds of bugs in your apps. What tool(s) to use depends on what you are building.
Personally, speaking Elixir-specifically, I add type- and function-specifications to (nearly?) all ‘code for public consumption’ in my projects. However, I am not running Dialyzer (because of it). Nevertheless I view typespecs as an important and useful tool to communicate how to use the things I built in the documentation.

TypeCheck was built to build on top of this “type specifications as usage contracts” idea, by checking if a function really adheres to the contract that is specified. (i.e. “for any possible input the function claims to accept, assert that it does not crash and that it returns what it claims to return.”), which is a very simple and widely-applicable example of property-based testing.

That said, while developing TypeCheck I did encounter a couple of places where it is visible that typespecs were grafted on top of a pre-existing system. For instance, it is not possible using Erlang’s built-in typespecs to specify that a function returns a particular literal list (say “a list with two elements, the first being an integer and the second a string”), whereas these types see quite a bit of usage as input- or return-values in practice.

5 Likes