mmmrrr
So I thought a little bit about something I wrote earlier in this topic: If you could change one thing in Elixir language, what you would change? - #102 by mmmrrr
TL;DR; I complained about the refactorability of Elixir, compared to statically typed languages.
I am a huge fan of the Elm compiler. It helps me to figure out the correct data model for my problem domain by allowing to change everything without the fear of introducing hard to debug edge cases.
This is what I’m looking for in this question: how to write Elixir code in a way that makes it easy to refactor, without emulating a type system with unit tests?
I’d like to stay away (for now) from compile to BEAM languages, like Alpaca, Gleam, Purerl and Elchemy, since none of them feel ready to me. Also I love the Elixir ecosystem and would really like to keep it as is.
End of prologue.
I know about typespecs. But they feel a little like typescript: as soon as you start to interfere with the world, it kind of falls apart. Also they don’t provide any protection at runtime and don’t enforce totality (meaning: you have to implement each and every possible code path).
So how are you solving this problem? Generate guard clauses from your typespecs? Testing your code into oblivion? Ignore it, since you feel the added, initial productivity of a dynamic language is worth the maintainability issue?
I’m specifically interested in your opinion @OvermindDL1 ![]()
(P. s. this is meant to be a discussion thread on how to improve refactorability in Elixir code, in the hope that we can extract common best practices of the community to be helpful for others, so feel free to share how you are doing it)
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
rodrigues
have you tried dialyzer?
asianfilm
I’m in a similar situation/mindset, in that I’m coming from Elm and a type-driven-development approach. And I’m similarly keeping an eye on Gleam, etc.
I have a 6,000+ line Elixir codebase that is under heavy development. I spent today introducing Chris Keathley’s Norm library, which is influenced by the Eiffel’s language’s design by contract philosophy.
Norm won’t give me the same developer experience as Elm, but I think it will help me find errors more quickly and more explicitly. It’s certainly helping me think more clearly about the shape of my data. (I’m not using Ecto, just transforming data in a pipeline.)
It’s too soon for me to have a strong opinion on Norm. I’m still finding my own coding style when using the library. It’s really making me miss Elm’s “Maybe”, or rather it’s forcing me to be explicit about what data can be nil. For example, when expecting a list from a third party, I’m now explicitly transforming nil to an empty list to keep my Norm specs simpler.
OvermindDL1
Gradualizer is coming, should help it along!
mmmrrr
Gradualizer looks indeed promising but is also not there yet, correct?
Yes that’s what I tried to imply with
. The gripe I have with it is that it’s very easy to put
typespecs- seems like I have to refactor the postany()anywhere (just like in typescript) and that you can simply ignore the tool completely if you don’t care. This proofs to be problematic in multi-member projects in my experience.I’m currently thinking that it’s probably not sensible to bolt on a typesystem onto a fundamentally dynamic language like Elixir. In a past job I tried that in Clojure and it was not worth it at all.
I thought that it could be interesting to generate guarded functions based on typespecs. So that you have your “happy path” function (which will always only be invoked with the correct parameters) and of course a “bad path” function that handles wrong input.
So, a little, like an enforced
Eitherbased on the typespecs.rodrigues
Not if you have
I’ve just put up a gist with a few pratical errors dialyzer can spot for you dialyzer_example.ex · GitHub
underspecsonDialyzer & Elixir experience is getting better over time (specially thanks to dialyxir), but there is still a lot of room for improvement for sure.
OvermindDL1
It’s already usable for a lot of tasks actually.
al2o3cr
In my experience, adding a strict typing system to an environment where your co-developers don’t care about quality only results in messy code that typechecks.
dimitarvp
Not sure how well that answers the original question but I gradually adopted my own guards plus typespecs, opting for
any()if Dialyzer proves too stubborn.And just use those in my modules. Furthermore, I am trying my best to enforce those during PR reviews.
IMO Erlang/Elixir are far too behind the real static typing and trying to bolt it on results in a lot of headache. Using pattern-matching and guards in the function heads has so far served me well enough in terms of invested efforts vs. errors caught before production data gets corrupted.
mmmrrr
N. b. I’m still trying to figure this out
@asianfilm have you formed an opinion on Norm in the meantime?
asianfilm
Hi @mmmrrr
Norm helped me gain confidence in the Elixir code I was writing, but it was a lot of extra work. I also introduced the Witchcraft Suite for Haskell-y “dark magic” for Maybe, semigroups and functors, finding ways to use it alongside Norm. My code got a lot more expressive but I also felt I was hitting the limits of my understanding of functional languages.
So I took a break studying Haskell for two months full-time before returning to Elm. And, after a year away from it, I returned a much stronger Elm programmer. Much of the data massaging I was doing in Elixir I’m now doing in a headless Elm REPL. I’m appreciating the static type checking and excellent type-safe libraries like elm-graphql.
I’ll probably return to Elixir in the coming weeks. I’m not sure if I’m ready yet to go full-Haskell on the backend and I still want to use Phoenix for Presence, OTP supervisors, etc. I’ll keep using Norm but I’ll be limiting the amount of data processing I’m doing in Elixir to be less dependent on it. Same for Witchcraft and it’s Haskell “fan-fiction”.
In summary, yes, I recommend Norm. I found it flexible, even in the library’s early days. I haven’t been following it’s development closely in recent months, so I’m curious to see how it’s matured. But the developer knows what he’s doing and judging from his podcasts is fully aware of Elixir’s strengths and weaknesses.