mmmrrr

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 :slight_smile:

(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)

Showing Posts 1 to 10

rodrigues

rodrigues

have you tried dialyzer? :slightly_smiling_face:

asianfilm

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

OvermindDL1

Gradualizer is coming, should help it along! :grin:

mmmrrr

mmmrrr OP

Gradualizer looks indeed promising but is also not there yet, correct?

Yes that’s what I tried to imply with typespecs - seems like I have to refactor the post :wink: . The gripe I have with it is that it’s very easy to put any() 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. :thinking:

So, a little, like an enforced Either based on the typespecs.

rodrigues

rodrigues

Not if you have underspecs on :+1: I’ve just put up a gist with a few pratical errors dialyzer can spot for you dialyzer_example.ex · GitHub

Dialyzer & Elixir experience is getting better over time (specially thanks to dialyxir), but there is still a lot of room for improvement for sure.

OvermindDL1

OvermindDL1

It’s already usable for a lot of tasks actually. :slightly_smiling_face:

al2o3cr

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

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.

defguard is_opts(x) when is_list(x)
defguard is_id(x) when is_integer(x)

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

mmmrrr OP

N. b. I’m still trying to figure this out :smiley:

@asianfilm have you formed an opinion on Norm in the meantime?

asianfilm

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.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews