<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark 17f8bc8 - Deploying to main from @ amaranth-lang/amaranth@ef2e9fa809fd8971820ac2ad8ad084ac9c8589d4 🚀
<whitequark>
so, hm
<whitequark>
I actually have an idea of how to add full-fledged pattern matching to Amaranth
peeps[zen] is now known as peepsalot
Sarayan has quit [Ping timeout: 260 seconds]
<robtaylor>
oooh
<whitequark>
it'd make the language simpler, even
<vup>
I am very intrigued
Sarayan has joined #amaranth-lang
<whitequark>
I'll show a draft sometime later
<whitequark>
but yeah I can see being able to pattern match e.g. structures
<whitequark>
the thing about Amaranth is that it sort of already has pattern matching; the lhs of a .eq is a pattern, but it's an irrefutable pattern and the bindings you can do are limited
<whitequark>
in terms of pattern matching, you're binding a temporary and immediately assigning it to a signal
<whitequark>
so what we can do is to add something like `Bind("x")` or whatever to denote this variable explicitly
<whitequark>
`Bind("x")` would be possible to use on both lhs and rhs; when used on lhs, it binds the name, and when used on rhs, it returns the binding from the enclosing scope (so the innermost `Switch` that binds it)
<whitequark>
internally it's just a temporary name, nothing interesting is happening there
<whitequark>
and since you can use `Bind` inside of `Cat`, you can do `with m.IfLet(Cat(0, Bind("x"), 1))`, which means you can do `with m.IfLet(SomeStruct.const(a=0, b=Bind("x"), c=1))` because that lowers down to `Cat`
<whitequark>
actually I think the .const I'm referring to here is a private method right now, but you get the idea; that's a natural thing for a Layout to have, a method that returns a Cat of the parts as appropriate
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark b762273 - Deploying to main from @ amaranth-lang/amaranth@57612f1dce9f5e35a915235ab442d477b5f8db59 🚀
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark be2cd6d - Deploying to main from @ amaranth-lang/amaranth@de36e3c162707bf0e376d90774d31c13a54eedae 🚀
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark 2e9cb06 - Deploying to main from @ amaranth-lang/amaranth@7ea2e175e457c03a248c106f2c2ed7d9c9448310 🚀
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark b6a8edb - Deploying to main from @ amaranth-lang/amaranth@0b7adcbd10597536e3e0386f3524e91c013f6510 🚀
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark 89d7c18 - Deploying to main from @ amaranth-lang/amaranth@9ec7f5b5078903320e7f706d059d5e0178196d51 🚀
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark 94e6763 - Deploying to main from @ amaranth-lang/amaranth@0ee5de036c5d2697d066c9450bf42f15fef64d18 🚀
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark c0c7f82 - Deploying to main from @ amaranth-lang/amaranth@35561ea11a30f18942ed9d6e3b3941a4e02f0b17 🚀
<_whitenotifier-9>
[amaranth-lang/amaranth-lang.github.io] whitequark 7adc788 - Deploying to main from @ amaranth-lang/amaranth@14e73a73de54a4bc2e7db54b3bfa5803a088daee 🚀
<cr1901>
What's an irrefutable pattern? One that always matches whatever's on the RHS (like using a variable name in a match arm in Rust)?
<whitequark>
yeah
<cr1901>
I think I'll wait for the draft. I'm not sure I understand what Bind("x") gets you to as opposed to "using x directly" in your examples
<whitequark>
oh, it's just scoping
<whitequark>
let's consider a simplified version of this
<whitequark>
x = Signal(); with m.Switch(...): with m.Case(Cat(0, x, 1)):
<cr1901>
Yup, so far, so good
<whitequark>
now that we accept Cat() in Case(), we can also bind x when it matches, right?
<whitequark>
so there'd be an implicit x.eq(...[1:-1]) or something like that
<whitequark>
that has to be inside the Case
<cr1901>
ahhhh
<whitequark>
but with this snippet, you have to declare x yourself, and you probably don't want that, e.g. if ... is a generic thing
<whitequark>
so `Bind("x")` is letting Amaranth do it itself
sensille has quit [*.net *.split]
mwk has quit [*.net *.split]
Ekho has quit [*.net *.split]
XgF has quit [*.net *.split]
<cr1901>
And it'll make sure the variable shadowing works right (refer to the "x" in Case, not the "x" at the beginning of the snippet)
<whitequark>
yeah
Ekho has joined #amaranth-lang
sensille has joined #amaranth-lang
mwk has joined #amaranth-lang
XgF has joined #amaranth-lang
<whitequark>
you might also want to do something `with m.If(v.matches(Bind("x")):`
<whitequark>
actually we might not even need an IfLet construct
<whitequark>
it's enough to just use Bind in a conditional
<cr1901>
Actually wait, I retrace that "shadowing" statement; the "x = Signal()" at the beginning is so that the "m.Case(Cat(0, x, 1)):" portion works, i.e. they are the same signal
<cr1901>
retract*
<cr1901>
Bind("x") removes the need for that
<cr1901>
Is that more correct?
<whitequark>
yeah
<cr1901>
But Bind will allow you to create shadowed bindings too- i.e. in an inner Case/Switch/If/whatever, Bind("x") will refer to "the most recent "x" that has been bound". I don't think I've ever tried shadowing an existing Signal binding within a Switch/Case/If scope, so no idea if that works :P.
<whitequark>
yes
<whitequark>
and the latter doesn't exactly work
<whitequark>
maybe a better idea is, hm
<whitequark>
`with m.Case(Cat(0, Bind("bits"), 1)) as pm:` and then `pm.bits` has the signal thing
<cr1901>
I personally like that a bit more. It's more explicit in how the bindings work/minimizes shadowing confusion (now you have to name your Bind() _and_ the context manager the same thing :P)
<cr1901>
the same thing in two different scopes*
<whitequark>
it naturally extends to If
<whitequark>
`with m.If(v.matches(Cat(0, Bind("bits"), 1))) as pm: pm.bits`
<cr1901>
Lastly, can you elaborate on "and you probably don't want that, e.g. if `...` is a generic thing"." I'm confused about what "if `...` is a generic thing means" (because that "eq" would be internal to how Cat() works, Bind() or not, correct?)
<whitequark>
well if the width of ... varies then you need to do something like x = Signal(len(...) - 1 - 1) (for each other Cat operand)
<cr1901>
ahhhh
<cr1901>
and if you add Cat operands, w/ varied width, welll
<cr1901>
yea, that sounds like it gets ugly quick
<cr1901>
>and if you add Cat operands, w/ varied width, <-- okay, maybe "don't do that", but the point remains; anything beyond "constant width Switch expression, constant number/width of Cat operands" is ugly to do manually, and Bind() will abstract away all that mess for you :)
<cr1901>
Thanks for the clarification :)
<whitequark>
actually now that i think again, you'd have to give Bind a shape
<whitequark>
so it's more like `with m.If(v.matches(Cat(0, Bind("bits", unsigned(2)), 1))) as pm: pm.bits` or something