wmnnd

wmnnd

Function parameters vs Keyword lists

A little while ago, I suggested to add Ecto.Changeset.optimistic_lock/4 in addition to the already existing Ecto.Changeset.optimistic_lock/3. While José didn’t like the idea, he also pointed out that if he were to consider it, he’d prefer modifying the API so that it would accept an opts Keyword list instead of adding optimistic_lock/4.

Ever since that, I have been wondering: When is it preferable to use an opts Keyword list instead of additional function parameters? What criteria do you usually use to decide which one to pick?
In many cases so far, I have found Keyword lists to be less convenient than »regular« parameters since you can’t do pattern matching on them.

My impression is that they might be better for public-facing interfaces in order to have a cleaner API. I’d then extract the values in the public function and work with them in additional private functions where pattern matching can be used again. What are your thoughts on this? Any rules of thumb that you employ when making the decision?

Most Liked

michalmuskala

michalmuskala

In general I would say that, for public functions, whenever you get more than 3 or 4 arguments you should really look into making them all (or at least some of them) a keyword. As @Qqwy mentioned many positional arguments are error prone.

Another, not mentioned yet, but very important use case are boolean arguments. Positional boolean arguments are a horrible API (most of the time), using keywords makes it slightly better since you’re able to name the thing.

Pattern matching on keywords is indeed problematic, but there are ways they can be used cleanly. A solution that I really like and often see in Erlang code, but rarely in Elixir, is reducing over the keyword to build some sanitised state.

Qqwy

Qqwy

TypeCheck Core Team

Keywords are useful if:

  • A function has many parameters, and remembering the order of these is hard. Using a keyword list is nice in this case, since it shows at a function call location what information is used for which purpose inside the function.
  • A function has many optional parameters.
  • A function takes some arguments from the list of options, and passes the rest on. (Note that while this is nice in some cases, it is often a bad idea as it does not ‘fail fast’; you won’t notice right away if you made a typo in the option name, for instance)

An important drawback of keyword lists is that when you ‘just’ use them, your function might be passed without some argument that it needs, so depending on where your function is used, you might want to add some descriptive error messages if people forget to pass it.

As you indeed already mentioned, not being able to pattern-match on them, as the keywords might be passed in any order is another drawback. Therefore, while they are nice in an outward-facing API, internally you’ll probably end up calling a high-arity function anyway.

So, my rule of thumb: Use them when it makes the public-facing code more clear, and use them when there are many optional arguments.

michalmuskala

michalmuskala

You can see an Erlang example in how poolboy process builds its state:

https://github.com/devinus/poolboy/blob/master/src/poolboy.erl#L125-L146

I can’t think of elixir examples right now.


UPDATE: You can see an elixir example in this refactoring of credo (that didn’t actually make it, but shows the idea quite nicely):
https://github.com/rrrene/credo/pull/107/files#diff-6a5ceb395f2a140c73ab593bf06fcb55

https://github.com/michalmuskala/credo/blob/b0811c97f5a509593755f6c7b7c44d6f8d18b3ff/lib/credo/cli.ex#L122-L143

Where Next?

Popular in Discussions Top

CharlesO
Erlang :list.nth simple, but 1 - based nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). Elixir Enum.at … coo...
New
PragTob
Hey everyone, this has been brewing in my head some time and it came up again while reading Adopting Elixir. GenServers, supervisors et...
New
chulkilee
Here are the list of HTTP client libraries/wrappers, and some thoughts on HTTP client in general. I’d like to hear from others how they w...
New
Fl4m3Ph03n1x
Background A few days ago I was listening to The future of Elixir from Elixir Talks, with Dave Thomas (@pragdave ) and Brian Mitchell. I...
New
eteeselink
Hi all, In the last days, two things happened: A blog post titled “They might never tell you it’s broken” made the rounds. It’s about ...
New
mmmrrr
Just saw that dhh announced https://hotwire.dev/ Is it just me or is this essentially live view? :smiley: Although I like the “iFrame-e...
New
paulanthonywilson
I like Umbrella projects and pretty much always use them for personal Elixir stuff, especially Nerves things. But I don’t think this is ...
New

Other popular topics Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
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 31525 112
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 49134 226
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

We're in Beta

About us Mission Statement