dogweather

dogweather

Idiomatic, real-world Elixir resources?

Can anyone recommend books/courses/videos that use real-world Elixir? E.g.:

  • Idiomatic error handling design, whether it’s {ok/error, ...} or something else.
  • Uses specs as they “ought” to be, however that is.
  • Uses the various mechanisms for creating types such as @type, defstruct, and @enforce_keys.
  • Test-first coding generally, if that’s common.
  • Idiomatic module and function naming.
  • Use of keyword lists in function signatures.

(The resource or two I’ve purchased teach good architecture, apparently, but are not idiomatic, unfortunately.)

First Post!

Nefcairon

Nefcairon

Sadly you haven’t got any answers. I cannot give you any either, sorry.

To be honest, though having bought many Elixir books, I haven’t read half of them as the last months I get slowly “detached” from Elixir because of the fact that the ressources always lack at least one of this aspects, if not many.

If I continue learning Elixir, I want to learn it as “right” as possible. Haven’t wrote any elixir code for two months now. It’s a pity, as the language is great and this forum is great, too. I hope something brings my enthusiasm back I haven’t had since rails 15 years ago.

Most Liked

dimitarvp

dimitarvp

This is definitely partially against my principles because I don’t believe in universal truths most of the time. But I’ve allowed myself to compile a list of those possible idiomatic good practices – which IMO go well beyond Elixir itself.

  • Use @spec. However I’ll strongly recommend against that during rapid prototyping. It will just get in the way. The moment you find a module haven’t changed in 2 weeks however, go put specs on its functions and start chasing the green output of dialyzer.

  • Complement @spec with your own guards. If you know a datatype you use is actually a list, DO NOT guard it with is_list(param). Define a guard and use that instead:

@spec orders :: [%Order{}]

defguard is_orders(x) when is_list(x)

def eligible_for_discount?(orders) when is_orders(orders) do
  #...
end
  • Do not overdo docs. Many times they should even be superfluous. If your function is well-named and the parameter names hint at their purposes clearly enough you should be just fine by putting something like Checks if this batch of orders is eligible for a corporate discount or even without docs at all. Module docs are mostly important for widely used libraries. For your own open-source pursuits and commercial projects they are mostly a distraction. Go for GitHub’s wiki articles, or Atlassian’s Confluence pages, etc.

  • Don’t use import if you can help it. Resist it with all your might. Only use it if you have no other choice and your code will not work otherwise. If you really must use it, use the :only option.

  • Don’t overdo case and cond. Use function heads with hardcoded parameter values, say def length([]), do: 0 for example. Readability often means less coding lines (though certainly not always; too much brevity can make your code very cryptic but that’s a bigger topic I won’t tackle here).

  • Use macros and don’t be shy about it – but use them sparingly. For example when you want to transcode stuff and writing all functions manually would be tedious: that is a very valid usage. Macros should be used to make an otherwise annoying code generation easier and quicker. Some here in this forum get tempted to use them to introduce their own extra syntax in Elixir but I am very against this; these efforts usually attempt to emulate another language’s constructs and rarely seem aimed at actual productivity (at least not in the way I understand it; I realise this is a controversial opinion). They are usually used to rekindle nostalgia for paradigms the author fondly remembers from other languages. I am personally strongly opposed to these temptations; if using macros can both reduce the coding lines and improve readability then I’ll use them. But I am not a fan of the LISP approach where people make their own DSLs which are perfectly suited for the project… and nobody else except the author can maintain them or the project after they are introduced in it. We have to keep our work maintainable. That’s the professional courtesy I want to show to future maintainers of my code and thus I resist using macros in Elixir beyond simple readability improvement and reducing otherwise tedious coding work.

  • GenServer, Agent and many OTP primitives and derivatives should be used for (a) keeping state and (b) making your system resilient to partial failures. If you find yourself using them for other purposes then you are abusing them and should reconsider your approach.

  • As noted above, I like @enforce_keys for structs, provided I am not in the rapid prototyping phase. I will prefer compile-time errors to runtime errors 99% of the time.

  • Some extraneous code is acceptable if it makes using your modules more ergonomic and intuitive. For example I regularly make wrapper Config modules for my apps and libraries where I expose both generic put / get functions and specific put_* and get_* functions (say, put_timeout or get_batch_maximum). I’ve had colleagues argue with me over this and I’ll concede this is a personal preference but to me the intuitive reading of my code almost as if it’s English should win over small extra amounts of code.


I am probably missing several others but hopefully this gives you an idea of what I find to be good practices. Apologies for this becoming rather huge.

sasajuric

sasajuric

Author of Elixir In Action

I agree with app vs library point, but I’m not sold on the conclusion. I don’t have enough of a sample to know what “the wast majority of applications” does, but in my limited experience of trying both approaches for a couple of years, I definitely prefer app code with specs, because they help me understand what are the allowed inputs and what are the possible outputs, which in turn reduces the uncertainty when reasoning about the code.

Consider for example the following function:

def create_account(params)

What is this function accepting and what is it returning? To know this we need to look into the implementation. If we’re lucky, the impl is simple and we see everything in that function’s body. But quite often, we have to dig through a bunch of other private functions to piece it all together. This is cumbersome, energy draining, and error prone.

Consider in contrast the spec version:

@type account_params :: %{
        first_name: String.t(),
        last_name: String.t(),
        email: String.t(),
        password: String.t(),
        role: Role.t()
      }

@spec create_account(account_params()) :: {:ok, Account.t()} | {:error, Ecto.Changeset.t()}
def create_account(params) do

This already tells us so much more without forcing us to read the entire implementation. That is in my view a key benefit of typespecs. Combined with proper naming and well defined responsibilities they provide great help in reasoning about the code, library or app.

This is why I prefer to say that types are first and foremost about documenting, not about catching bugs. When a type checker complains, it means that there is a discrepancy between the spec and the implementation. From the standpoint of the desired program behaviour we don’t know what is wrong (in fact the program might even be correct), but we do know that the documentation (spec) doesn’t match the code, so we need to fix something to sync them.

I agree though that one should not be fanatical. I typically spec only API functions (i.e. exported funs which are not marked with @impl or @doc false), except for modules such as Phoenix controllers or Graphql resolvers, because specs don’t really help here at all. I might occasionally spec a private function if I estimate that it’s interface is complex and the inclusion of specs will assist the reader.

When it comes to docs/moduledocs, I don’t write them in the app code unless there are some important implications not obvious from the name/spec combo.

lucaong

lucaong

My suggestion, if you feel you know Elixir well, but you want to step up your practical Elixir coding skills, would be to read the codebase of some quality open-source projects. The source code of Elixir core library is a great place to start, as well as delving into libraries that you use, and possibly contributing to them.

Subjective opinion here: I would not stress too much about what is idiomatic or not, but rather develop a sense for which codebases are easier to navigate and why. Reading other people’s code helps in this respect, because we always approach it as “beginners” with little context. As developers, I think we focus too much on code style on a superficial level (stuff that linters can enforce) and too little on deeper aspects of what increases/decreases cognitive load.

Last Post!

sorentwo

sorentwo

Oban Core Team

To clarify, my conclusion was that in my “real-world” experience people don’t tend to write specs for applications, not that they shouldn’t write specs. Personally I’m in favor of it for precisely the reasons you mentioned.

My feelings exactly.

Where Next?

Popular in Chat/Questions Top

maqbool
what books/Resources do you recommend to learn about distributed system(theory)?
New
OmanF
In the attached screenshot (taken from an exercise in “Programming Elixir”) can be shown that BugReport is defined as a struct that, itse...
New
makeitrein
More Ecto questions! More madness! The context: there’s a list of books that I want to filter with a dropdown… The dropdown: looks some...
New
Fl4m3Ph03n1x
I am doing some exercises while learning Elixir using Exercism.io. Now, my objective is to do all exercises, extras included. This shoul...
New
maz
I’m getting this error: ## ** (Ecto.Query.CompileError) Tuples can only be used in comparisons with literal tuples of the same s...
New
satoru
I’m working on the “Bob” exercise on the Elixir Track in Exercism. I am testing for uppercase letter with this simple check: c in ?A..?Z...
New
stevensonmt
I’d like to provide my review of the Elixir Course module from Groxio. I have some criticisms but I’d like to start with the positives. ...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31586 112
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New

We're in Beta

About us Mission Statement