vic
Asdf Core Team
Expat is a tiny experiment I did for extracting patterns and being able to reuse them (compose and share patterns between elixir libraries). Look for zombies in the README.
It was born as some of my first attempts at creating composable things like the just released data specification library Spec.
Hope anyone finds it interesting at least.
Trending in Announcing
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries.
offset-based pagination with...
New
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
I released Doggo, a collection of unstyled Phoenix components.
https://github.com/woylie/doggo
Features
Unstyled Phoenix components....
New
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
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
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Hello
Published a new library - ProcessHub!
ProcessHub is a library designed to manage process distribution within the Elixir cluster. ...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi everyone!
The first release candidate for the Expert language server project is now available!
We’ve published a press release detai...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
This showed up on my feed.. anyone heard of it? Just hype?
Ox Alpha is a reasoning model designed for coding, sustained ag...
New
Hey folks,
I just published a post about Hologram’s funding and where the project goes next - the short version:
Curiosum as Main Spons...
New
:microphone: ElixirConf 2026 - Call for Talks is open!
We’re heading to Chicago :united_states:
:round_pushpin: In person + virtual
:d...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex










Showing Posts 32 to 23- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
marciol
I’m thinking that expat can be a simple way to improve readability and also it allows a way to do a sort of contract check at function boundaries. Is this the way to follow or the Elixir community are agreeing on other approaches?
josevalim
Follow-up question: how do you know that in the natural number case you need to wrap each subpattern inside
{:nat, val}but you don’t have to in the maybe case? Given one sayszero(0)and the other saysnothing(nil), I was expectingzero()to generate0and not{:nat, 0}.I also assume that when using unions you can no longer do something like
def value(maybe())? I guess not doing that makes sense though, as the different subpatterns may have different shapes/number of arguments.josevalim
Hi @vic,
I really enjoy the continuous work you have been doing in expat!
I wonder if we should support
whenclauses inside patterns and automatically move them out. So if you do:It becomes:
while nobody would write the first in practice, it is very powerful for extending the language. It means you would be able to drop the
expatprefix in many cases.Similarly, regarding the union types, I wonder if instead of the expat prefix, you could use the prefix of the union, so this example becomes:
Or even:
Although I am not sure how that would play with nesting. For example, if you have a
maybewith anatinside, how would you handle it? Do you need two separate cases? I guess that would be the case, as anatwith a nothing doesn’t make sense. I wonder if other languages (OCaml/Haskell/F#) allow case to be “folded” in such cases or do they require an explicit case per pattern? /cc @OvermindDL1Thoughts?
OvermindDL1
I saw! I also left a comment/question on that commit.
vic
Yep, added your additional
Maybeexample, thanks. Also added aResult one.
OvermindDL1
Oh now those are really cool! Full matching and so forth without enforcing a structure, even the Maybe is just natural in elixir (still not big on the
nilword, but eh, Elixir’isms).EDIT1: In the
maybetest, you have this test:What about having this too:
That shows you can extract as well.
EDIT2: How would you implement a Result type that accepted an
:ok,{:ok, value},:error, and{:error, reason}as an example? That would show a variety of usages of using native types and no wrapping and so forth, preferably usingok/0/error/0/ok/1/error/1and such constructors.vic
v1.0.4features Union Patterns. Patterns composed into a single one, this lets you emulate things like:OvermindDL1
What my defguard did was you could define something like:
This could then by used like:
And it does expand as expected. Specifically since
_excis passed into theis_exception‘guard’ then the_excin the argument list gets a matcher added to it, while it also added the guard in theis_exception(_exc)position. You could even match ‘out’ something by passing in more arguments in some custom defguard, but essentially the above example turned into:It basically just allows treating a guard as something that can both match and guard at the same time. This is more powerful than just what expat does now because currently expat can only add a single matcher and N guards, where with my defguard pattern above it allows for N matchers and N guards, like in this convoluted example:
Or whatever, the thing is that you can pass in multiple things, and notice that you can pass ‘out’ as well:
So here you can see that it passes ‘out’ something as well, and you just put a binding there. Notice that you can put a specific value there too like:
So now that will only match
{a, :ok}whereais an atom. The thing is that instead of just being 1-matcher/N-guards, it is N-matchers/N-guards, and since it is in the guard position then you can usein_guard?/1as usual to do the right action if in a guard or make it an expression, which means you could use it in statement positions properly like:And notice that you can match on yet ‘more’ things as well. I could easily imagine adding the capability of having it support static KWLists so you could do things like this too if you want:
Or whatever as well.
Yep, same with the defguard style too.
Instead of just supporting single matchers, what about supporting multiple?
I’m not saying replace the existing functionality with this, but rather ‘add’ the defguard stuff to expat as well, then it would have both the ability for N-Matchers/N-Guards via a ‘guard’ and for easy-inline 1-Matcher/N-Guards via the matcher style too.
Expansion is not limited to being used as part of when, it can be used as an expression with defguard as well as it essentially just appears as any normal function call.
vic
Oh btw, generated macros now get documented.
vic
I think I don’t understand what you mean with multiple matchers, could you provide an example of what syntax you use in yours and what would you want it to expand into?
Let me ellaborate a bit on how expat expands maybe could be useful for other people following this thread:
When you define
defpat foo(1), the ast inside foo (here1) is the code that will be placed at call-site. eg.foo() = 1expands to1 = 1.That means it’s actually possible to place any elixir code in there, and the
foomacro will just expand it when called.Now, since
expat’s purpose in life is to help with pattern matching,the ast inside foo is treated specially in the following cases:
if it contains a variable like
defpat foo(x)then x is boundable by the caller of foo. The caller can bind it by name, like:foo(x: 1) = 1=>1 = 1If x is not bound by the caller, like
foo(), x will be replaced with an_, sofoo() = 1is_ = 1if it contains a guard like
defpat bar(y) when y > 10then, the code of the guard will also be expanded, for example:bar(y: 2)will expand toy = 2 when y > 10.Note however that since we have a guard to check, and
yis being used in it, the variableyis preserved in expansion, however thisyis higenic (elixir’s counter distingishes it from others) and will not bind any other y in your own scope.To bind in your scope you do something like
bar(y: u)expands tou = y when y > 10anduis a variable you provided from your scope.So, you could bind bar’s
ywith any expression, even other pattern expansions (just regular function calls)bar(y: z = foo(x: 20))will expand toy = z = 20 when y > 20this will also work:
bar(z = foo(20))since expat now supports positional arguments (variables get bound in the order they appear on the pattern)If it contains a nested pattern expansion. For example, if you had
defpat t2({x, y}) when x > yand later diddefpat teens(t2(bar(a), bar(b))) when a < 20 and b < 20Then
teenshas two bindable names,:aand:band it will get expanded into a pattern like:{a, b} when a < 20 and b < 20 and a > b and a > 10 and b > 10That means inner guards get propagated into the calling expansion.
Now since
defpatjust captures the code inside the pattern for expanding it later,defpat named(%{"name" => name})allows you to expandnamedanywhere you can place a pattern in elixir, like on the left hand side of=named(x) = %{"name" => "vic"}will expand to%{"name" => x} = %{"name" => "vic"}, that’s why you can use it on a function definition like:def index(conn, params = named(name)), do: ...However, for those containing guards,
def lalala(teens(m, n))would by itself expand into:def lalala({m, n} when m < 20 and n < 20 and m > n and m > 10 and n > 10)Of course having a
whenin that context fails.as it would do if you try:
So, having guards was what introduced the
expat defsyntax:expat def lalala(teens(m, n))expands correctly into:def lalala({m, n}) when m < 20 and n < 20 and m > n and m > 10 and n > 10.Finally, that’s why
expatis different fromdefguardboth elixir’s and @OvermindDL1’s because for expat, expansion is not limited to be used as part of awhen. Named expat patterns can be used anywhere it’s valid to expand it’s containing expression.So, hope that helps anyone.
If someone is interested, you can read more examples in the tests, and the docs, or use the source.