gameline

gameline

Hi,
I was surprised I couldn’t find any argument on this on the net so here I am.

When you make recursive function that deal with list and want to pattern match argument against [] or [head|tail] you can:

function [], do: *something*
function [h|t], do: *something else*

which is what I would call the Elixir way. A more haskellish way might be:

function argument do
  case argument []
    [] -> something()
    [h|t] -> something_else()
  end
end

The first option allow you to handle different arities the same way but is there other good arguments on why you should use the first option instead of the second ?

Showing Posts 15 to 6

rvirding

rvirding

Creator of Erlang

It makes no difference at all with regards to efficiency. Literally the complier translates it into almost the same code with the main difference being what error you will get.

dimitarvp

dimitarvp

I am aware, thank you. We have the right to disagree with some of them. :smiley:

As suggested above, I’d nest the deconstruction of a more complex piece of data, meaning I’d do a partial reconstruction at the top-level function(s) and then pass parts of it to nested ones where they also reconstruct. Huge deconstruction heads indeed are not easy to visually parse sometimes.

Cool, I disagree with that one too. :smiley:

IloSophiep

IloSophiep

I just want to mention that I think what you are “calling smelly” is actually the preferred refactoring for one of the anti-patterns in the Elixir documentation: Complex extractions in clauses. It might be worth thinking about it once more and weighing off the pros and cons. At least for me it was something that looked like a weird recommendation at first, but I started liking it. But maybe I misunderstood your point completely…

Edit: I just realized that’s pretty much what @JohnnyCurran already stated with

dimitarvp

dimitarvp

Nah, not saying that, I am saying that FunctionClauseError is a symptom of a very early stage problem. Hence, having everything be in the function header is a non-issue for me.

As for them growing big, yes, I can see that. But I would rework that into smaller functions e.g. in your example I would pass attrs to sub-functions.

Not at all, these are genuine ergonomics that can and have tripped people – myself included – so it’s worth debating especially if we end up in the same team.

JohnnyCurran

JohnnyCurran

I understand and I do empathize. I often find myself wanting to unwrap everything in the function head. I’ve found, however, after over 4 years of production elixir, that it causes more problems than it solves

in particular, how do you square:

function head pattern matching is an all-or-nothing

with:

When the sizes are manageable it’s very easy to tell which function head was attempted

At a certain point, especially on a team, the sizes of parameters are going to get large. It’s inevitable. is it all-or-nothing, or is it a manageable size?

And, to be honest I’m not sure I totally understand this question:

Why do we get FunctionClauseError at this non-early stage of the project still

If you are suggesting we all write error-free code, I agree, but I don’t know how practical that is. FunctionClauseErrors aren’t exclusive to third-party apis, and, like everything in software – things change. To suggest “just don’t write code that throws a FunctionClauseError” seems misled

In any case – I have genuinely enjoyed this discussion with you as I love discussing the ins-and-outs of Elixir with whoever is unfortunate enough to be within earshot :slight_smile: , especially so with those whom which I disagree. There is no growth without disagreement. All that to say that I hope my tone has not come off as disrespectful which I know it has the possibility to do over text, especially over the wider internet :slight_smile:

dimitarvp

dimitarvp

Yeah, same thinking. If there’s anything I can relegate to the computer to fail early so I can fix it quickly and move on, I’ll do it. I’ll take this to the absolute extremes. (To the point that I worked with comby and semgrep several times to try and detect anti-patterns in code; hey, if I succeed then maybe that’s one thing I can charge money for consulting, who knows!)

Rant blurb: I really feel people often forget “computers must serve us, not us serving them”. (And this is not pointed at the other poster @JohnnyCurran, just a general complaint.)

dimitarvp

dimitarvp

Eh, yes and no. If function heads grow that big so as troubleshooting becomes difficult, this outlines two serious problems:

  1. Why do we get FunctionClauseError at this non-early stage of the project still? Those bugs should have been fixed already. They are early-stage “we are still not sure what this external API might return” things. After one month in production these should be gone.
  2. When the sizes are manageable it’s very easy to tell which function head was attempted, especially having in mind that Elixir and the monitoring system will also show you the function arguments that were attempted.

So you’re not wrong on the outset but I’d argue that the issues you mention absolutely should be non-issues at this lifecycle stage of the project.


To address your next comment: sure, that’s valid. I suppose for part of the scenarios that technique can help. I still would not though, to me function head pattern matching is an all-or-nothing. When we start mixing both that + matching in body it gets confusing and code-smelly to me. I’d rework it.

JohnnyCurran

JohnnyCurran

Yes, precisely, and I think a good example that may illustrate what I’m trying to say a little better may be –

Let’s consider for a moment that my app is capable of communicating with Google Bard as well as OpenAI,

My LiveView may make a function call like this:

MyContext.complete_chat%{messages: messages, ai_service: :open_ai})

In my context, I unwrap only what’s necessary to determine which function head to take:

def complete_chat(%{ai_service: :open_ai} = attrs) do
  %{messages: messages} = attrs
  # call openai api here
end

def complete_chat(%{ai_service: :google_bard} = attrs) do
  %{messages: messages} = attrs
  # call google bard here
end

MatchErrors now point me directly to the specific function head where the error occurred

adw632

adw632

Definitely prefer the function head approach, declarative programming at its finest.

You give me this and I return that, no if’s, cases or maybes.

JohnnyCurran

JohnnyCurran

Elixir will only show the line number of the first defined function head (on a FunctionClauseError). It will also log each function head it attempted to take - if each function head is unwrapping several values that are orthogonal to the flow control, it becomes difficult to tell which one should have been taken.

That leaves the developer in the unfortunate position of figuring out

  1. Which function head should have been taken with this call?
  2. Which missing value caused this function invocation to fail?

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
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
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
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews