Open timotheecour opened 6 years ago
I think it's hard to support if
. How would the compiler know that a macro should be invoked? For case
it's simple since the type of the expression decides it. Could you give an example of how ifStmtMacros
would work?
In a v2 I'd like to have assignment support when deconstructing, maybe via a special prefix symbol like @
:
case ("foo", 78)
of ("foo", @a): echo $(a*2) # prints 78 * 2 = 156
of ("bar", 88): echo "no"
else: discard
@GULPF @araq on second thought, ifStmtMacros
are not needed, maybe we can just use a regular macro (which itself could share code with macro match
perhaps, idk):
let expr = ("foo", 78)
if expr.matches(("foo", _)): discard # matches a tuple of length 2
if expr.matches(("foo", )): discard # matches a tuple of length 1
if expr.matches("foo"): discard # same as if expr == "foo"
# works with matching against derived type, allowing also to capture derived instance
if expr.matches(Employee(name: "bob"), myderivedinstance): echo myderivedinstance.name # same as C# example `if (bassam is Employee {Name: "bob"} myderivedinstance)`
this is not specific to if
and could be used in any context where a boolean is expected, eg:
when expr.matches(("foo", _)) : discard
doAssert expr.matches(("foo", _))
let ok = expr.matches(("foo", _))
macro matches(pattern: untyped) = untyped # will evaluate to bool
macro matches(pattern: untyped, capture: untyped) = untyped # will evaluate to bool and capture derived pattern in `capture`, as in C# and example above
IMHO these proposals should start by providing a good example of current Nim code that would benefit from it. Is there something that gets written time and time again that would improve with this? (also including readability not only terseness when writing the code).
I do think that deconstructing is a good tool though, but often times we just want to guard and alias to locals, which is an use case that can be solved more cleanly with something like this I think:
if let a = ("foo", a):
echo $(a*2)
if let _ = ("foo", _): # `_` is just used to force deconstructuring
echo "hi!"
# from the original comment
# case let a, b = ("foo", 78)
# of ("foo", a): echo $(a*2) # referencing `b` would produce an error, not in scope
# of ("bar", c): echo "no" # error: `c` is not part of the case let statement
# else: discard
# this I think is better
case ("foo", 78)
of let a = ("foo", a): echo $(a*2)
of let _ = ("foo", _): echo "ok" # forced matching
of ("bar", 80): echo "no" # no destructuring or matching
else: discard
Alternatively, a fully featured match
expression could be nice but I think that would change how the language is used quite substantially.
For reference also scheme's matchable is implemented as a macro and is quite flexible.
Seems like Rust-like if let
, while let
, case let
for deconstruction + assignment is coming back quite often (and this fits a similar needs to the new Python :=
I feel).
The most ergonomic pattern matching I came across was Haskell there are 2 syntaxes, unfortunately the nicer one does not fit Nim at all (though the second one is very similar to Nim's).
Syntax 1:
take 0 _ = []
take _ [] = []
take n (x:xs) = x : take (n-1) xs
Syntax 2
take m ys = case (m,ys) of
(0,_) -> []
(_,[]) -> []
(n,x:xs) -> x : take (n-1) xs
isn't this {.experimental: "caseStmtMacros".}
gonna run into https://github.com/nim-lang/Nim/issues/8676#issuecomment-413943400 ?
@yglukhov does your proposal https://github.com/nim-lang/Nim/issues/8676#issuecomment-414665222 address this for caseStmtMacros?
I like the https://github.com/nim-lang/Nim/issues/8649#issuecomment-413323627 idea, if one can capture a submatch
Pattern matching is a big ergonomic win, there are so many if a.field == and a.field.b == and a.other ==
which will look so much better with a matching syntax
@krux02 's macro in his ast pattern lib can also be made to work on runtime values, we just have to agree on some kind of standard API
With the features that are already there in Nim I implemented recursive pattern matching fo AST nodes here: https://github.com/krux02/ast-pattern-matching.
Please get to the point and explain why the given language features are not enough. The issue is just a compilation of random ideas and in general quite a mess.
Whatever comes out of this, I would argue that it should be enough to support both patty and AST pattern matching (both suitably adapted)
The problem is that both @andreaferretti 's patty and @krux02 's ast-pattern-matching currently lack a lot of features, but in a very different way: e.g.
now, I really want to have a working lib:
on the other hand the patty and the ast-pattern-matching dsl-s differ .. but not greatly. this means we might have two slightly different dsl-s for the same thing in an already small ecosystem.
Do you guys want to compromise on a single api and somehow combine the two libs approaches into a single , more universal nim pattern matching lib?
Such a lib can still be
I worked before on personal forks of both libs, but I couldn't decide which would be more feasible to adapt (and I didn't have time). I also planned to make my own match macro, but then we will just have 3 libs (insert xkcd standards comic)
I would certainly want a library that generalize both patty and @krux02s' library. I am also not especially attached to patty: if a better library comes out that does pattern matching, I will be happy yo deprecate it. It was just a small experiment, but it is by no means a complete solution.
so I decided to test my ideas in https://github.com/alehander42/gara
@andreaferretti I got the idea for many features from patty , especially unification, I am thinking of a more powerful variant of it: e.g. support @[@x, expression(@x)]
(to check expressions based on captured names), do you think this would be useful?
@timotheecour I also implemented your matches idea, but I am not sure about the API: I have a.matches(pattern)
which returns bool and a.maybeMatches(pattern)
which returns an option with a tuple[name: value..] with the captured names(see more details in README). I feel that's cleaner than passing a capture arg?
Fusion pattern matching does not seem to have "custom matching" or "deconstruction", but it seems plenty sufficient for most pattern matching (Some(@x)
=> (isSome: true, get: @x)
). If not, assigns allows for overloading unpacks based on type (matches are an abstraction over unpacks, so not as efficient), but I don't like to advertise this library because fusion matching is a lot more comprehensive and will be more stable.
Fusion pattern matching does not seem to have "custom matching" or "deconstruction", but it seems plenty sufficient for most pattern matching.
Agreed, I think this RFC has been superseded by #245. fusion/matching
implements all of the originally proposed features (recursive matching, _
placeholder and matching in if
via the matches
macro).
For "custom matching" or "deconstruction", most mechanisms can be customized (write procs to support object patterns, write an items
iterator to support seq patterns, write a kind
proc to support variant patterns, etc.)
see https://gist.github.com/Araq/169d1e24b2b996d024a780ef6a4e6c09#file-casestmtmacros-nim for context, introduced by @Araq for a 1st version of this concept.
IT reminds me of Recursive Patterns matching (Deconstruction Pattern) in C#8, see https://www.infoq.com/articles/cs8-ranges-and-recursive-patterns
recursive could be handled later.
But supporting
_
placeholder is very useful, would be nice to support it~~furthermore,
if
could be handled as well, it should be no different fromcase
in terms of capabilities (ie introduce alsoifStmtMacros
) analog to howif
andcase
both handle pattern matching in C#~~ EDIT see reply below; this is better handled with a regular macromatches
where?
matchers.nim
links
snippet copied over from @araq's post