Does nix-lang have structural pattern matching?

In order not to derail Darwin, again … - #51 by michaelCTS , I’ll ask the question here: does nix-lang have structural pattern matching similar to python, rust?

For example this is possible in rust (syntax might not be correct, but you get the idea)

match platform, arch {
  "darwin", * => darwin_package,
  "openbsd", "x86_64" => openbsd_x86_64_package,
  * => default_package
}

builtins.match is for regular expressions and the doc doesn’t point out the existence of a switch of something similar.
If I’m not mistaken, nix-lang is a functional programming language and pattern matching came from that paradigm.

Nix has patterns in function arguments, which can be either:

  • a single value binding (of any type - scalar, list, attrset, …)
  • attributes of an attrset, destructured to single bindings (with ... to allow more arguments, and ? to allow defaults for missing attrs)
  • a combination of the two, via the @ syntax.

See: Language Constructs - Nix Reference Manual for more details.

It does not, as far as I’m aware, have the control-flow / switch style match construct using those patterns. Please tell me if I’m wrong on this point, or if there’s a useful library function to approximate it. Since functions are anonymous and lazy, I guess you just use a function.


Rust syntax aside

the example you gave looks like this:

match (platform, arch) {
  ("darwin", _) => darwin_package,
  ("openbsd", "x86_64") => openbsd_x86_64_package,
  (_, _) => default_package
}

but the idea was otherwise clear, indeed.

Nix doesn’t have pattern matching as builtin syntax, but nixpkgs.lib has attrsets.matchAttrs, and you can kind-of build pattern matching with that:

# nix repl  --extra-experimental-features 'flakes repl-flake' nixpkgs
nix-repl> if_let = v: p: if lib.attrsets.matchAttrs p v then v else null

nix-repl> if_let { platform = "darwin"; arch = "aarch64"; } { platform = "darwin"; }
{ arch = "aarch64"; platform = "darwin"; }

nix-repl> if_let { platform = "darwin"; arch = "aarch64"; } { platform = "linux"; }  
null

nix-repl> match = v: l: builtins.elemAt (                 
            lib.lists.findFirst (                         
              x: (if_let v (builtins.elemAt x 0)) != null   
              ) null l                                    
            ) 1

nix-repl> match { platform = "linux"; arch = "aarch64"; } [
            [ { platform = "darwin"; } "it's macOS" ]         
            [ { platform = "linux"; } "it's Linux" ]
          ]
"it's Linux"

Is this what you’re looking for?

1 Like

I think this would be a great addition to the lib! Looks exactly like what I’m looking for. Thank you!

1 Like