Pattern matching against arbitrary sized tuple

How can I pattern match against an arbitrary sized tuple? This works:

{status, _, _} = {:ok, 1, 2}

But what if I don’t know the structure of the tuple on the right side and I only care about the first element? This doesn’t work:

{status, _} = {:ok, 1, 2, 3, 4, 5, 6}
{status, _} = {:ok, {:random, :things}, [1, 2, 3]}

Is it possible to get the first value from the tuple with pattern matching?

No. A tuple is of fixed size.

You can either use status = elem({:ok, 1, 2, 3}, 0) or [status | _] = Tuple.to_list({:ok, 1, 2, 3}.

6 Likes

Tuple.to_list/1 will allocate a new list, Kernel.elem/2 can be used to retrieve the first element without creating this intermediate list :slight_smile:

https://hexdocs.pm/elixir/Kernel.html#elem/2

4 Likes

would be handy to have some macro that generates the missing underscores:

 {:ok, info} <- :file.read_file_info(pid),
 {:file_info, file_size, _, _, _, _, _, _, _, _, _, _, _, _} <- info,
{:file_info, file_size, _escape_next(12)} <- info,

for that particular tuple, I think you can use the Record module.

Thanks, this makes the code much more readable, not much examples for this module, if someone needs one for this particular case:

defmodule FileInfo do
  require Record
  Record.defrecord :file_info, Record.extract(:file_info, from_lib: "kernel/include/file.hrl")
end

and then instead my previous line I have a keyword list that can be accessed with

iex(3)> import FileInfo
FileInfo
iex(4)> info
{:file_info, 2333891, :regular, :read, {{2020, 12, 29}, {19, 18, 12}},
 {{2020, 12, 29}, {19, 27, 58}}, {{2020, 12, 29}, {19, 27, 58}}, 33188, 1,
 66310, 0, 302070, 33, 19}
iex(5)> file_info(info)
[
  size: 2333891,
  type: :regular,
  access: :read,
  atime: {{2020, 12, 29}, {19, 18, 12}},
  mtime: {{2020, 12, 29}, {19, 27, 58}},
  ctime: {{2020, 12, 29}, {19, 27, 58}},
  mode: 33188,
  links: 1,
  major_device: 66310,
  minor_device: 0,
  inode: 302070,
  uid: 33,
  gid: 19
]
iex(6)> file_info(info, :size)
2333891

1 Like