问题
I'm trying to create some examples of using (experimental) macros this way:
use experimental :macros;
macro cards_vars() {
(<hearts clubs diamonds spades> X~ 1..10).map: { "my \$$^x = False;" }
};
cards_vars();
say $hearts1;
This creates and runs the macro, and then checks if one of the variable defined exists. But I get this error:
Too few positionals passed; expected 3 arguments but got 2
I don't even know where that error comes from. I think it's in cards_vars(), but I have no idea if that's the case or not. Declaring the macro without the parentheses yields the same error.
回答1:
Update: To be clear, macros are not just experimental but very rudimentary and buggy. For a little more about this, see Moritz's answer to another macros question.
Let's start with a golf of your code:
use experimental :macros;
macro foo { 42 }
foo
This yields the same compile-time error:
Too few positionals passed; expected 3 arguments but got 2
You get this error if you return anything other than an AST object from a macro.1 Which is reasonable given that this is the entire point of the macro
construct. The error message is less than awesome -- but then again macros are an experimental feature.
So you need to return an AST. Here's one way:
use experimental :macros;
macro foo { AST.new }
foo
This yields the run-time error:
Useless use of constant value foo in sink context (line 3)
This shows that the macro has done its job. The compiler has completed compilation and proceeded to run-time.
Explicitly returning AST
objects is the wrong way to proceed. The first reason is given on the AST
doc page:
There is no API defined for ASTs yet. Hopefully that will emerge as part of the work on macros.
The second reason is that there's a higher level construct for constructing AST
objects, namely the quasi { ... }
construct3 mentioned on the doc page and by Scimon:
use experimental :macros;
macro foo { quasi { 42 } }
say foo; # 42
The quasi
block tells the compiler to compile the enclosed statements into AST form.
Note that the result isn't an AST corresponding to a block. In the above it's an AST object corresponding to 42
.
So, finally, back to what you were trying to do.
Simplifying:
use experimental :macros;
macro foo { quasi { my $bar } }
foo;
say $bar
yields:
Error while compiling ... Variable '$bar' is not declared
Whereas:
use experimental :macros;
macro foo { quasi { class bar {} } }
foo;
say bar; # (bar)
So, bottom line, lexical declarations currently fail to stick.
The experiment hasn't gotten that far.
Carl Mäsak, the creator of the experimental macros in Rakudo, is continuing the experiment at 007. If you leave an issue at its GH repo Carl will answer.
1 When the compiler encounters a call to a routine that it knows is a macro2 it replaces the macro call with its result. In more detail the compiler:
Compiles all the macro's arguments into AST form;
Calls the macro (at compile-time) passing it the arguments in their AST form;
Runs the macro routine which is expected to return an AST object (er, "ASTish");
Splices the resulting AST(ish) object into the overall AST being constructed.
If you don't return an AST(ish) object the last step goes wrong with the error message in your title.
2 Currently, if you put the macro definition after the call then the compiler doesn't realize it's a macro and treats it as an ordinary routine:
use experimental :macros;
say foo; # 42
macro foo { 42 }
say bar; # AST.new
macro bar { quasi { 42 } }
say bar; # 42
It would presumably be better if this caused a compile-time error.
3 Carl, the macros champion, likes the word quasi
. It draws from lisp heritage which draws from mathematics heritage. Imo it is a "too cute" choice that's a less than awesome fit for P6. My rationale is that Perl philosophy suggests choice of keywords with familiar meaning to newbies. The word "quasi" has a well known normal English connotation of meaning "sort of, but not really". That's not at all helpful. (I have suggested toAST
to Carl but he prefers quasi
.)
回答2:
So a bit of poking about and I found this old halloween article : https://perl6advent.wordpress.com/2012/12/23/day-23-macros/
Which gives some pointer. You need to use quasi
to highlight the code you're going to output. But getting it to create a variable? Not sure. I'm still setting if it's possible.
来源:https://stackoverflow.com/questions/53875026/too-few-positionals-in-macro-definition