Support arity syntax in definition patterns - #10
Merged
Conversation
dannote
requested changes
Jul 20, 2026
dannote
left a comment
Collaborator
There was a problem hiding this comment.
I like the arity syntax, but there are two consistency issues to sort out.
| ) | ||
| when form in [:def, :defp, :defmacro, :defmacrop] do | ||
| with {:ok, caps} <- match_def_head_arity(shead, name_pat, arity_pat, caps) do | ||
| do_match(srest, prest, caps) |
Collaborator
There was a problem hiding this comment.
The examples say def name/2 should work on its own, but prest is empty in that case and srest contains the body, so it never matches a normal definition:
Patcher.find_all(
"def apply(a, b), do: {a, b}",
"def name/2"
)
# []Could you either make the short form ignore the body or update the API/docs to require def name/2 do ... end?
|
|
||
| defp match_def_name(_name, _name_pat, _caps), do: :error | ||
|
|
||
| defp name_atom_value(name) when is_atom(name), do: name |
Collaborator
There was a problem hiding this comment.
This changes the capture from the identifier AST ({:foo, nil, nil}) to :foo. Besides being inconsistent with existing definition captures, it breaks repeated captures:
Patcher.find_all(
"def foo(foo), do: foo",
"def name/1 do name end"
)
# []Could we keep the same capture shape as existing definition patterns?
`def name/2`, `def _/0`, `def name/_` constrain a definition by argument count. `/N` is an exact arity, `/_` matches any, and the name is captured (or `_` to ignore it). Works across def/defp/defmacro/defmacrop and matches guard clauses.
rodrigues
force-pushed
the
vr/arity_syntax
branch
from
July 21, 2026 14:48
70bc2c9 to
c24e475
Compare
Address two consistency issues in `def name/arity` definition patterns:
- Capture the name as the identifier AST `{name, nil, nil}` rather than a
raw atom, matching how every other pattern capture binds AST nodes. This
fixes repeated captures, so `def name/1 do name end` matches
`def foo(foo), do: foo` and rejects `def foo(bar), do: bar`.
- Document that the arity form mirrors the parenthesized head form's body
handling: `def name/2 do ... end` behaves like `def name(_, _) do ... end`,
and body-presence must line up on both sides. The bare short form matches
only bodyless defs, exactly like the bare paren form. No matching change;
moduledoc now shows the arity syntax with a `do ... end` body throughout.
dannote
approved these changes
Jul 22, 2026
# Conflicts: # lib/ex_ast/pattern.ex # test/ex_ast/pattern_test.exs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Definition patterns can now be constrained by argument count with
name/aritysyntax./Nis an exact arity,/_matches any arity, and the name is either captured or ignored with_:def name/2def, capturing the namedef _/0def, name ignoreddef name/_defof any arity, capturing the namedefp name/1defmacro name/2Works across
def/defp/defmacro/defmacrop, respects the definition kind (defp name/1won't match a publicdef), and looks through guard clauses (def clamp(x) when x > 0is arity 1). Empty-paren heads likedef ping()are treated as arity 0.Before
name/2was matched literally as a / operator, so it never lined up with a definition head.After
_on the name is a non-capturing arity constraint — same matches, no name: binding:Fixed-shape definition patterns (def name(a, b), def ()) are unchanged.
_(_)doesn't count when there are guards, Maybe we want to change that?Arity constraints are opt-in via the / syntax.