-
Notifications
You must be signed in to change notification settings - Fork 23
Rename case statement macro from match to case
#332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It surely does look like a good idea. |
Will this break the pattern matching macro ?. |
Yes most likely, a compromise could be made where both |
We should make them non-experimental while we're at it. |
I haven't found any bugs related to case statement macro, so I could say this feature is ready to be enabled by default. |
|
@Araq @hlaaftana I have match "abc"
of "foo": discard
of "bar": discard
else: discard and then I'd know this would pickup the custom one. After this change, it's unclear whether the custom one or builtin one is used; if you have imports and compilation in the mix, this gets worse. The fact you can only define the macro via a single name ( proposalimplement # for
macro customFor(x: ForLoopStmt): untyped = ...
for a in [1,2].customFor: ...
# case OLD STYLE, deprecated
macro `case`(n: tuple): untyped =
case "foo"
of "foo1": discard
else: discard
# case NEW STYLE
macro customCase(n: CaseStmt): untyped = ...
case "foo".customCase
of "foo1": discard
else: discard this seems strictly like a better idea:
|
I don't think import macros
{.experimental: "caseStmtMacros".}
macro match(x: int) =
# or x: untyped
echo x.repr
let x = 3
case x
of 3:
echo "good"
else:
echo "???"
# output: good The existing
type CustomCase = distinct string # or CustomCase[T] = distinct T. actually works
template customCase(str: string): CustomCase = CustomCase(str)
macro `case`(customCase: CustomCase): untyped = # ...
case "foo".customCase
of "foo1": discard
else: discard |
I originally considered that but decided that the use cases are different enough not to bother with this idea. But maybe I was wrong. |
that would be a very odd restriction; there are valid use cases for custom when defined case4:
let val1 = "foo1"
let val2 = "foo2"
case "foo1".customCase
of val1: discard
of val2: discard
else: discard
this is too complicated, see https://gist.github.com/timotheecour/f7a847b0f0e9909efd6f7ee85420c4be for a working but ugly example using this, it requires a lot of silly boilerplate, eg: when true:
import macros
from typetraits import distinctBase
{.experimental: "caseStmtMacros".}
type CustomCase1[T]= distinct T
template customCase1[T](a: T): CustomCase1[T] = CustomCase1[T](a)
proc `==`[T](a: CustomCase1[T], b: T): bool = a.distinctBase == b # {.borrow.} won't work
# more proc's may need to be defined in more complex cases just to support `distinct` approach
macro `case`(n: CustomCase1): untyped = ... it also prevents conditionally importing symbols, eg via Instead with the proposal #332 (comment), those problems are avoided and it's much simpler: when true:
import macros
{.experimental: "caseStmtMacros".}
macro customCase(n: CaseStmt): untyped = ...
my modified proposal is as follows: when true:
import macros
{.experimental: "caseStmtMacros".}
macro customCase[T](a: T, b: CaseStmt): untyped = ... then you have everything you want:
macro customCase(a: untyped, b: CaseStmt): untyped = ... notethe same is true for macro customFor[T](a: T, b: ForLoopStmt): untyped = ... neither of what I've suggested is a breaking change btw. |
That would be great for macro customCase(a: string, branches: varargs[untyped]) = ...
customCase "foo1":
of val1: discard
of val2: discard
else: discard You literally save characters by not having to type |
TIL about the fact you could do that, it's pretty interesting;
macro `case`(a: string, branches: varargs[untyped]) = ...
# build lookup table, etc (potentially, also support RT branch values if we want to extend its support)
case "foo1":
of val1: discard (but the Note also that example 1nim-lang/Nim#8821 could potentially be fixed in library code instead of in the compiler (jsgen)
(or, if we don't change the builtin, we can now at least provide a suitable library defined alternative that would fix this) example 2it'd allow accessing the value we're case-ing over, using custom logic defined in library code: case foo.bar[2]:
of 1..2: echo caseVal # accesses `foo.bar[2]` |
Object variants. Not the prettiest part of Nim (though the idea is good), but it is meant to be tied to That feature is "post-expression blocks", I don't know where I learned it from, in the documentation it's only in the grammar. On top of |
import std/[macros]
macro customCase(a: string, branches: varargs[untyped]) = newLit(12)
let res1 = case "foo1":
of "12": 12
of "13": 12
else: 22
# expression '12' is of type 'int literal(12)' and has to be used (or discarded)
let res2 = customCase "foo1":
of val1: 12
of val2: 12
else: 22 # invalid indentation error
customCase "foo1":
of val1: discard
of val2: discard
else: discard If those two are fixed, I would prefer I think the original goal of this RFC
is more than enough, though support for |
that's not true, see below which works; you just need to add a return type to your macro, eg: when true:
import macros
macro customCase(a: string, branches: varargs[untyped]): untyped = newLit(16)
let res2 = customCase "foo1":
of val1: 12
of val2: 12
else: 22
doAssert res2 == 16 The only thing missing is allowing an indentation, which IMO is a minor point I can live without (idiomatic style is to not indent case..of), and in any case could be supported in future in the parser, if needed.
my point is we can deprecate |
Yes, and I knew this when I added caseStmtMacros, however:
|
Closing, issues for |
That's not true, see #332 (comment) which has working code. The other point ( this discussion was prematurely closed. The current situation is not good:
proposalwe should do the proposal I gave earlier in #332 (comment) which is better in all respects than the current
case customMatch(x)
of ...
As mentioned earlier, it follows the |
It's a 2 line fix. Anyone can do it. macro `case`*(n: untyped): untyped =
result = getAst(match(n)) If you're saying that there's a problem with match being renamed to case, then I don't know what to tell you, fusion not fixing it is fusion's problem. And fusions development cycle or whether or not we should get rid of case statement macros and add more syntax hooks are overgeneralizations. I think 18 comments are too many on an issue about changing a single string in the compiler. |
as i said above:
that's not my point. My point is that this rename |
It would never work? {.experimental: "caseStmtMacros".}
import macros
macro match(x: (int, int)): untyped =
echo x.treeRepr
match (1, 2):
of (2, 3): discard
|
i meant:
so |
To hopefully carry the discussion here at least a tiny bit more toward closure after all this time, this current working syntax wasn't mentioned: match case a
of b: discard
else: discard
# with parentheses, maybe could be made more lenient in some places
x = match(case a
of b: 1
else: 2)
foo match(case a
of b: 1
else: 2)
# works with this syntax too
match case a:
of b: discard
else: discard
# can even add arguments, whatever this means
foo arg, case a
of b: discard
else: discard
let x = foo(arg, case a
of b: 1
else: 2) This isn't good enough for standard pattern matching (for which the plans seem to have shifted to putting at language level anyway), but cheap custom match implementations can easily use this and not have to post expression block stuff. The same logic can be applied to for loop macros, which is essentially the same as what let a = [10, 20, 30]
var b: seq[(int, int)]
enumerate for i, x in a:
b.add((i, x))
assert b == @[(0, 10), (1, 20), (2, 30)]
let c = "abcd"
var d: seq[(int, char)]
enumerate 97, for (i, x) in c:
d.add((i, x))
enumerate(start = 97, for (i, x) in c:
d.add((i, x)))
assert d == @[(97, 'a'), (98, 'b'), (99, 'c'), (100, 'd')]
unroll for i in 1..5:
const j = i
echo j For the sake of the argument, assume no one thinks these are ugly and statement-syntax-as-expressions is considered standard (or there is some new, again not considered ugly, unambiguous syntax like
So stuff like So I am personally rejecting whatever reason this RFC was reopened 2 times for, and hoping people are satisfied with what they have now, and if they're not they can make their own new RFC or work on language level pattern matching or whatever. Or bump/review RFCs and PRs with similar ideas like nim-lang/Nim#19383. |
Uh oh!
There was an error while loading. Please reload this page.
https://nim-lang.github.io/Nim/manual_experimental.html#case-statement-macros
The macro required to implement a custom case statement has to be named
match
. This is a very common name for macros and is usually used directly instead of viacase
. Nim allows stropping keywords for routine names. So, I proposematch
should be changed to`case`
. The example becomes:This experimental feature seems to be seldom used as people prefer to just use custom matching macros. This would help outline the difference and rid any incompatibilities (
n: tuple
is not actually AST of a tuple, it's the AST of a case statement). Further improvements could be done to this feature, for example changing the arguments(n: tuple)
to(n: tuple, branches: varargs[untyped])
, but I think renamingmatch
to`case`
first will make further changes much more straightforward.Relevant line in compiler:
https://github.com/nim-lang/Nim/blob/25c75752d07fccc00682890875f3ce6c13f61d90/compiler/semstmts.nim#L867
The text was updated successfully, but these errors were encountered: