Closed haxscramper closed 3 years ago
Typo on line 53 "mathc" should be match. (Github wont let me mark it for some reason)
If you can add more examples that would be great IMHO. :)
@juancarlospaco you mean more examples in documentation specifically (e.g. just copying some of them from tests), or coming up with more additional usage scenarios and putting them to documentation?
Whatever you feel like doing, but just more actual code examples that you can copy-paste and they work, like the one I suggested.
The explanations, descriptions and pseudo-code are more than enough, but just 1 example thats actual code that you can copy-paste and play with.
You're probably aware, but the footnotes are broken (i.e. they don't link), but this seems to be an issue in the RST generator.
I think it should be clarified that ref objects can be matched just like normal object (at least it seems to work), the current documentation makes it sound like you need to use of
to match ref objects.
@konsumlamm to organize list of different implementation details that we've discussed (on discord, other issues etc)
Option
s in form of Some(Some(<pattern>))
as opposed to just Some(@variable)
(as it is done now). <pattern>
can be a single variable capture - @variable
.(field: <pattern>)
is supported and has the same behavior as (field: is <pattern>)
. You edited this out to your comment on #245, but I guess it is better to have more intuitive syntax work too.expr
and pos
gensymedref
objects when/if it is necessary to use of
Additional pattern match guards in form of [@a, .._] if a == 1:
as opposed to [@a == 1, .._]
- using if
as infix operator is not possible, so it would have to be something like [@a, .._] and a == 1:
which might be supported, but I suppose it could be confusing (additional way of doing the same), and it doesn't play well with sequence operations, because [all @start < 10]
requires single iteration and fails earlier as opposed to [all @start] and allOfIt(start, it < 10)
or something like that.
If some of your points are missing - I probably forgot to add something, it wasn't intentional.
If you have any code snippets that you would like to work (like Some(Some())
that you wrote when testing pattern matching (or any other things that you would like to see working) - feel free to just drop them in as a comment in this discussion, I would put them in unit test.
EDIT: and also add following points to documentation
var
) is an implementation detail and will be changed to view types when stable
Some(Some(<pattern>))
Some(<pattern>)
FTFY.
Make sure that
(field: <pattern>)
is supported and has the same behavior as(field: is <pattern>)
. You edited this out to your comment on #245, but I guess it is better to have more intuitive syntax work too.
This seems to work, however I don't see the point of supporting is <pattern>
in the first place. Or is it because @a is <pattern>
works, to complement == 42
?
Implementing custom unpackers - they are supported but not properly documented
Do you mean defining a proc and then matching (unpacker: <pattern>)
? That is nice, but I still think we can do better.
Additional pattern match guards in form of
[@a, .._] if a == 1:
as opposed to[@a == 1, .._]
- usingif
as infix operator is not possible, so it would have to be something like[@a, .._] and a == 1:
which might be supported, but I suppose it could be confusing (additional way of doing the same), and it doesn't play well with sequence operations, because[all @start < 10]
requires single iteration and fails earlier as opposed to[all @start] and allOfIt(start, it < 10)
or something like that.
Hmm, I see, perhaps we could at least replace @a(<it expression>)
with a custom template that gets exported from the module (to reuse the @a.predicate()
mechanism). My biggest problem here is introducing a magic it
variable.
Do you mean defining a proc and then matching
(unpacker: <pattern>)
? That is nice, but I still think we can do better.
I mean
type
UserType1 = object
fld1: float
fld2: string
case isDefault: bool
of true: fld3: float
of false: fld4: string
UserType2 = object
userFld: UserType1
fld4: float
proc `[]`(obj: UserType1, idx: static[FieldIndex]): any =
when idx == 0:
obj.fld1
elif idx == 1:
obj.fld2
elif idx == 2:
if obj.isDefault:
obj.fld3
else:
obj.fld4
else:
static:
error("Indvalid index for `UserType1` field " &
"- expected value in range[0..2], but got " & $idx
)
proc `[]`(obj: UserType2, idx: static[FieldIndex]): any =
when idx == 0:
obj.userFld
elif idx == 1:
obj.fld4
else:
static:
error("Indvalid index for `UserType2` field " &
"- expected value in range[0..1], but got " & $idx
)
block:
(@fld1, @fld2, _) := UserType1(fld1: 0.1, fld2: "hello")
doAssert fld1 == 0.1
doAssert fld2 == "hello"
block:
(fld1: @fld1, fld2: @fld2) := UserType1(fld1: 0.1, fld2: "hello")
doAssert fld1 == 0.1
doAssert fld2 == "hello"
block:
((@fld1, @fld2, _), _) := UserType2(userFld: UserType1(fld1: 0.1, fld2: "hello"))
doAssert fld1 == 0.1
doAssert fld2 == "hello"
And https://github.com/nim-lang/fusion/blob/ea18f8559c514b227b148300a2900b3e2a282b0d/tests/tmatching.nim#L1326 - overload []
for static[FieldIndex]
. But this is missing from documentation - this should be fixed.
FTFY.
Some(Some(<pattern>))
Some(<pattern>)
We talked about nested optional patterns too, and they should be supported.
I don't see the point of supporting is
in the first place. Or is it because @a is <pattern>
works, to complement== 42
?
The following patterns are identical because both 42
and == 42
are 'final expressions' - (integer/string/float literals) - they won't be parsed further, and pasted as-is. Any expressions in the form of <prefix-operator> <something-else>
is a final expression, unless <prefix-operator>
is any of the following: is
, of
. is
was added only because it didn't make sense to me to make it a hard error. of
is used for derived object matching.
These matches are identical wrt. to generated code
(a: 42) := (a: 42) # Final expression without prefix operator, compared for equality
(a: is 42) := (a: 42) # `is <pattern>` with pattern being `42` (integer literal, final expression).
(a: == 42) := (a: 42) # Final expression with prefix operator, pasted as-is
These generate different code
(a: (@a, @b)) := (a: (1, 2)) # `(@a, @b)` is not a final expression, it will be decomposed further
(a: (1, 2)) := (a: (1, 2)) # `(1, 2)` not a final expression, each element will be matched separately
(a: == (1, 2)) := (a: (1, 2)) # Final expression, `.a` will be matched using `== (1, 2)`
But I understand your point about is
being confusing - to be honest I was just stuck with this idea of using is
explicitly, but it doesn't make much sense in the end, so I will update documentation/examples to just use (fld: <pattern>)
when possible.
My biggest problem here is introducing a magic
it
variable.
Sequence matching was modeled after std/sequtils
and it is a common idiom in other templates/macros. Explicit is better than implicit and variable injection should be used with care, but I think this case is justified.
This PR is ready to be merged as I haven't found anything else that needs change
The first example in the documentation is still broken afaict (see review at the top).
Given that https://github.com/nim-lang/Nim/pull/16923 was merged, will you add a `case`
alias to match
?
With new release candidate near - https://forum.nim-lang.org/t/7494, and update for `case`
I think this can be merged now
I also added some patterns from https://www.python.org/dev/peps/pep-0636/, just to test how similar they are overall.
CI failure seems to be completely unrelated: Error: Archive 'windows_x32.zip' could not be found and/or downloaded. Maybe your OS/architecture does not have any prebuilt available?
Misc documentation and implementation changes for pattern matching.