ijverig

ijverig

Using guards like this works fine:

iex(1)> defmodule M do
...(1)>   def update_position({pos, max_pos}) when pos < max_pos, do: pos + 1                     
...(1)> end
{:module, M, …
iex(2)> M.update_position({3,4})
4
iex(3)> M.update_position({3,3})
** (FunctionClauseError) no function clause matching in M.update_position/1    
    
    The following arguments were given to M.update_position/1:
    
        # 1
        {3, 3}
    
    iex:2: M.update_position/1

But when extracting it into a defguard, the compiler gives a syntax error:

iex(1)> defmodule M2 do                                                                       
...(1)>   defguard is_valid_position({pos, max_pos}) when pos < max_pos                         
...(1)> end
** (ArgumentError) invalid syntax in defguard is_valid_position({pos, max_pos})
    (elixir) lib/kernel.ex:4623: anonymous fn/2 in Kernel.validate_variable_only_args!/2
    (elixir) lib/enum.ex:737: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:737: Enum.each/2
    (elixir) lib/kernel.ex:4592: Kernel.define_guard/3
    (elixir) expanding macro: Kernel.defguard/1
    iex:2: M2 (module)

Anyone has a clue of the problem here?

Showing Posts 1 to 10

NobbZ

NobbZ

You can not pattern match in a defguard.

ijverig

ijverig OP

As @NobbZ mentioned, the problem is that there’s no pattern matching inside a defguard header.
I didn’t find good documentation on this, though. Only some discussion prior to implementing defguard in v1.6:

defguard(h) is extremely limited in scope:

  • the function head cannot pattern match
  • code blocks are not allowed in guards
  • no assignment
  • no local functions

[1] Provide defguard · Issue #2469 · elixir-lang/elixir · GitHub
[2] Provide defguard · Issue #2469 · elixir-lang/elixir · GitHub
[3] Redirecting to Google Groups
[4] Defguard - experimenting with a structural matching alternative to Kernel.defguard

Follow up question: if I define a guard as a macro (according to the docs) then I can pattern match. However isn’t defguard a syntactic sugar for this macro?

NobbZ

NobbZ

Using a macro you can only pattern match on the AST, not on arbitrary runtime values.

ijverig

ijverig OP

But I was able to implement the original idea with a macro:

iex(1)> defmodule M2 do                                                 
...(1)>   defmacro is_valid_position({pos, max_pos}) do                   
...(1)>     quote do                                     
...(1)>       unquote(pos) < unquote(max_pos)              
...(1)>     end
...(1)>   end
...(1)> end
{:module, M2, …
iex(2)> defmodule M do                                                                         
...(2)>   import M2
...(2)>   def update_position({pos, max_pos}) when is_valid_position({pos, max_pos}), do: pos + 1
...(2)> end
{:module, M, …
iex(3)> M.update_position({3,4})
4
iex(4)> M.update_position({3,3})
** (FunctionClauseError) no function clause matching in M.update_position/1    
    
    The following arguments were given to M.update_position/1:
    
        # 1
        {3, 3}
    
    iex:6: M.update_position/1

This is because I’m pattern matching on the AST, not runtime values? Like: I’m pattern matching “structures” (like { }) of the language and types, not runtime values?

If it works on this macro, shouldn’t it work in the defguard expansion? Isn’t it just a macro as well?

NobbZ

NobbZ

You are matching on the AST of {pos, max_pos}, it would not work if you defined M.update_position/1 like this:

def update_position(p) when is_valid_position(p), do: elem(p, 0)

Because p has a different AST than the tuple which you specified literally before.

To be honest, the best way were probably to make this clear as “Klosbrühe” and defguard is_valid_position(pos, max_pos) when pos < max_pos or defmacro is_valid_position(pos, max_pos) do quote(do: unquote(pos) < unquote(max_pos)) end (maybe repair syntax of them).

Eiji

Eiji

@ijverig: You can solve this by:

defmodule M do
  defguard is_valid_position(tuple) when tuple_size(tuple) == 2 and elem(tuple, 0) < elem(tuple, 1)
end
NobbZ

NobbZ

Yeah that would work, but I prefer the 2 arity version as it makes no assumption about how pos and max_pos are related together.

In any case I’d add is_integer/1, is_float/1 or is_number/1 to the guard, it will safe a lot of time spent on debugging when the first time something not numeric slips into the tuple…

ijverig

ijverig OP

@NobbZ
Yes, I understand.
This was just an example, my need is more something like is_valid_position({x, y}, {max_x, max_y}) so I could call is_valid_position(pos, max_pos). IMHO changing that to is_valid_position(x, y, max_x, max_y) is a bit cumbersome on the readability.

But seems to be the way to go :slight_smile:

axelson

axelson

Scenic Core Team

@ijverig while I haven’t used it you may like the library Expat by @vic

Expat - composable, reusable pattern matching

OvermindDL1

OvermindDL1

Yeah this is a fantastic set, it essentially absorbed my defguard experiment (that would have worked as OP wanted if it was part of Elixir) but in a way that is stable. :slight_smile:

— All posts loaded —

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
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
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
mnkhod
So i have been using ash framework for a while and i love it. However currently the issue im having with ash framework is the error handl...
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