wmnnd

wmnnd

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?

Showing Posts 1 to 4

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

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

This sounds very interesting. How would this look, exactly? Would it implement reduction using primitive recursion (every time building up a one-higher arity function from the matched keyword), or in some way use e.g. Enum.reduce?

One other possibility would be to convert the keyword list to a map before pattern matching. Of course, one has to make a choice about how to handle duplicate arguments when doing that (first wins, last wins, accumulate in a list).

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

— All posts loaded —

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 91898 914
New
AstonJ
The obligatory hello world thread! Who are you and where are you from? :stuck_out_tongue:
4616 55835 594
New
byu
@chrismccord : I just saw the Extract AGENTS.md from Phoenix.new into phx.new generator commit to the phoenix project. My initial shotgu...
New
arcanemachine
I was working on an Ecto migration and I needed a timestamp. So, for the nth time, I looked up the different data types for timestamps, a...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New
Herve37
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using. We’re particularly inte...
New
matt-savvy
Is there a word for the ~> symbol used in Version strings? Do you also just call it a Squiggle Arrow™ ?!
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
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
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews