I've been trying to make an altered Version of Neel, which uses Jester and adds functionality. After registering some procedures that can be conveniently called from the front-end, you start the Neel app with a macro called startApp, which has this signature:
macro startApp*(startURL, assetsDir: string, portNo: int = 5000,
position: array[2, int] = [500,150], size: array[2, int] = [600,600],
chromeFlags: seq[string] = #[""], appMode: bool = true) =
...
startApp creates a Jester router with the router macro and some hard-coded routes:
router theRouter:
get "/":
resp(Http200,NOCACHE_HEADER,readFile(getCurrentDir() / `assetsDir` / startURL`))#is this most efficient?
get "/neel.js":
...
Now I've been trying to modify this and add a parameter to allow the caller of startApp to pass in extra routes, via an untyped "body" sort of parameter (is there a name for this, by the way?), like this:
macro startApp*(startURL, assetsDir: string, portNo: int = 5000,
position: array[2, int] = [500,150],size: array[2, int] = [600,600],
chromeFlags: seq[string] = #[""], appMode: bool = true,
extraRoutes : untyped
)=
... so you could do
startApp(startUrl="index.html", assetsDir="web", appMode=false):
get "/extra.js":
resp(Http200,`noCacheHeader`,"console.log('loaded extra.js')")
post "/login":
...
But now the above code leads to an error
Error: type mismatch: got <string, void>
but expected one of:
proc get[T](self: Option[T]): lent T
first type mismatch at position: 1
which means that the compiler is trying to evaluate the get expression instead of simply passing its unprocessed syntax tree into startApp, in contrast to The Whole Point of untyped macro arguments. I have figured out that it works fine if all the parameters are passed. So I guess the fact that I'm not naming the body is causing Nim to think I must be trying to pass it to portNo or something. Fair enough. But what do I do now? Is there any way I can do like extraRoutes=... here? I tried doing extraRoutes=quote do: ... and similar, but I couldn't find any way that works.
So... can this be fixed? Or will I have to go with a hack like manually passing in copies of the default args?
And if you have any better ideas for implementing this, I'm open, but please be detailed. I've been at this for something like five hours in total, I've already tried e.g. making a StmtList beforehand instead of tying this extra route business up in startApp, but gave up on that because the errors were even more cryptic.
Solution 1
Default arguments can be passed to macro, but for blocks it does not seem particularly pretty:
import std/macros
macro optArgs(arg1: static[string], arg2: static[string] = "hello", arg3: typed = nil, body: untyped = nil) =
if not isNil(body):
echo body.treeRepr()
optArgs(
"test",
body = (
quote do:
some untyped arguments that are not checked
)
)
optArgs("test")
optArgs("test", arg3 = int)
optArgs("test", body = int)
Outputs
Call
Ident "quote"
StmtList
Command
Command
Ident "some"
Command
Ident "untyped"
Command
Ident "arguments"
Command
Ident "that"
Ident "are"
Prefix
Ident "not"
Ident "checked"
NilLit
NilLit
Ident "int"
If you want to pass a block of code using call(<arguments>): body syntax, I would suggest the following approach: accept a syntax for tuple (can mix positional and named arguments) and doing some post-processing yourself. In this particular case you would get (Par (Ident "positional") (ExprColonExpr (Ident "test") (IntLit 123))) as an arglist, which can be processed to get necessary arguments, or you can call error to indicate misuse.
macro namedArgs(arglist: untyped, body: untyped) =
echo argList.lispRepr()
namedArgs (positional, test: 123):
echo "ACtual body"
Solution 2
One thing that should be noted is that tuple-like argument approach would give quite cryptic errors if argument is passed incorrectly (like Error: undeclared identifier: 'positional'). This can be fixed using
macro vras(arglist: varargs[untyped]) =
echo argList.lispRepr()
vras(positional, test = 123):
echo "ACtual body"
Which gives you the following tree as a result, but in this case, you would have to implement all argument handling manually.
Arglist
Ident "positional"
ExprEqExpr
Ident "test"
IntLit 123
StmtList
Command
Ident "echo"
StrLit "ACtual body"
Related
In an effort to learn Scala's macro system, I thought I'd try my hand at writing a basic CPS transformation macro. I've already written a fairly comprehensive CPS transformation framework for Clojure, so I'm pretty familiar with the CPS transform itself. However, I'm getting stuck transforming function / method applications.
For the CPS transform, function calls of the following form:
cps(f(<a>, <b>, <c>, ...))
need to be translated into an expression of the form:
cps(<a>){ $a =>
cps(<b>){ $b =>
cps(<c>){ $c =>
... => f($a, $b, $c, ...)
}
}
}
Obviously, the parameters of the generated continuation lambdas (e.g., $a) need to be fresh symbols so they can't inadvertently conflict with variable names in the lexical context. So for each argument arg, I generate a fresh name:
val name = Ident(TermName(c.freshName))
(where c is the macro's Context) which I then use in the following quasiquote:
q"""cps(arg)($name => $remainder)"""
where remainder refers to the remainder of the computation.
The macro itself compiles fine, but when I try to use it with an expression that involves a function application, I get the following error:
... exception during macro expansion:
[error] java.lang.IllegalArgumentException: fresh$macro$1 is not valid representation of a parameter, consider reformatting it into q"val $name: $T = $default" shape
However, I don't think it's possible to perform the recommended "reformatting", since there is no $default to provide.
Here's a minimal example that illustrates the issue I'm having:
def id[A](expr : A) : A = macro idImpl[A]
def idImpl[A](c : blackbox.Context)(expr : c.Expr[A]) : c.Expr[A] = {
import c.universe._
val name = Ident(TermName(c.freshName))
//c.Expr(q"""val $name = $expr; $name""")
c.Expr(q"""($name => $name)($expr)""")
}
Note that it works if you replace the lambda expression with the commented line.
So my question: How can a name generated by freshName be used as the parameter name for an anonymous function?
I believe you need a ValDef of the form val $name: $T = _, where the _ is represented by NoTree. Initializing variables with _ is valid Scala, setting references to null and numbers to 0 (i.e. the default values), but is also used as an internal placeholder when there are ValDefs (e.g. function parameters) but no sane default.
I’m on mobile here, so I can’t test it. In any case, you can destructure the value q"a: Any => a" in the REPL to find what the compiler normally sets as the default, if NoTree doesn't work.
I have a method that with the implicit parameter. i get a error when i convert it to function in 2 case :
1:
def action(implicit i:Int) = i + " in action"
val f = action _
then i get a StackOverflowError.
2:
def action(implicit i:Int) = i + " in action"
val f = action(_)
then i get a error: missing parameter type
I must write like this :
val f = (i:Int) => action(i)
that's ok. And if the parameter of 'action' is not the implicit , all case are right. So how to explain , and what i miss ?
If you specify a parameter to a function to be implicit, you are inviting the compiler to supply the value of that parameter for you. So how does the compiler find those values? It looks for values of the same type (Int in your case) that have been declared as implicit values in a variety of scopes.
(For simplicity, I'll just use a local scope in this example, but you might want to read up on this topic. Programming in Scala, 3rd Ed is a good first step.)
Note that the names of the implicit values are ignored and have no bearing on proceedings, the compiler only looks at the types of implicit values. If multiple implicit values with the required type are found in the same scope, then the compiler will complain about ambiguous implicit values.
For example, the following provides a function with an implicit parameter and a default value for that parameter within the current scope:
def greetPerson(name: String)(implicit greeting: String) = s"$greeting $name!"
implicit val defaultGreeting = "Hello" // Implicit value to be used for greeting argument.
val y = greetPerson("Bob") // Equivalent to greetPerson("Bob")(defaultGreeting).
val z = greetPerson("Fred")("Hi")
Note that y is just a String value of "Hello Bob!", and z is a string with the value "Hi Fred!"; neither of them are functions.
Also note that greetPerson is a curried function. This is because implicit parameters cannot be mixed with regular, non-implicit parameters in the same parameter list.
In general, it's bad practice to use common types (Int, Boolean, String, etc.) as values for implicit parameters. In a big program, there might be a lot of different implicit values in your scope, and you might pick up an unexpected value. For that reason, it's standard practice to wrap such values in a case class instead.
If you're trying to create a value that supplies some of the arguments of another function (that is, a partially applied function), then that would look something like this:
def greetPerson(greeting: String, name: String) = s"$greeting $name!"
val sayHello = greetPerson("Hello", _: String)
val y = sayHello("Bob") // "Hello Bob!"
val sayHi = greetPerson("Hi", _: String)
val z = sayHi("Fred") // "Hi Fred!"
In both cases, we're creating partially applied functions (sayHi and sayHello) that call greetPerson with the greeting parameter specified, but which allow us to specify the name parameter. Both sayHello and sayHi are still only values, but their values are partially applied functions rather than constants.
Depending upon your circumstances, I think the latter case may suit you better...
I would also read up on how the underscore character (_) is used in Scala. In a partially applied function declaration, it corresponds to the arguments that will be provided later. But it has a lot of other uses too. I think there's no alternative to reading up on Scala and learning how and when to use them.
I'd like to call a function returned by a function with an implicit parameter, simply and elegantly. This doesn't work:
def resolveA(implicit a: A): String => String = { prefix =>
s"$prefix a=$a"
}
case class A(n: Int)
implicit val a = A(1)
println(resolveA("-->")) // won't compile
I've figured out what's going on: Scala sees the ("-->") and thinks it's an attempt to explicitly fill in the implicit parameter list. I want to pass that as the prefix argument, but Scala sees it as the a argument.
I've tried some alternatives, like putting an empty parameter list () before the implicit one, but so far I've always been stopped by the fact that Scala thinks the argument to the returned function is an attempt to fill in the implicit parameter list of resolveA.
What's a nice way to do what I'm trying to do here, even if it's not as nice as the syntax I tried above?
Another option would be to use the apply method of the String => String function returned by resolveA. This way the compiler won't confuse the parameter lists, and is a little shorter than writing implicltly[A].
scala> resolveA[A].apply("-->")
res3: String = --> a=A(1)
I'm following a groovy tutorial and there is a code like this:
def fruit = ["apple", "orange" , "pear"] //list
def likeIt = { String fruit -> println "I like " + fruit + "s" } //closure
fruit.each(likeIt)
Eclipse reports an error at closure definition line:
Line breakpoint:SimpleClosuresTest
[line: 27] The current scope already
contains a variable of the name fruit
# line 27, column 14.
If i omit 'def' from 'def fruit' Eclipse doesn't complaint and code runs fine.
Could someone explain what is going on with the scopes in both cases?
Thanks.
first a general review of a groovy script:
// file: SomeScript.groovy
x = 1
def x = 2
println x
println this.x
is roughly compiled as:
class SomeScript extends groovy.lang.Script {
def x
def run() {
x = 1
def x = 2
println x // 2
println this.x // 1
}
}
in a groovy script (roughly speaking, a file without the class declaration), assigning a value to an undefined variable is interpreted as a field assignment.
your example tries to defines a closure with a parameter named fruit.
if you defined fruit with the def keyword you get an error message because the name is already taken as a local variable, and you can't duplicate a local variable name.
when you leave the def keyword out, you are actually assigning the value to a field of the class generated for the script, and thus the name fruit can be redefined as a local variable.
regarding scopes, it's pretty much like java...
in the example, you can see x is defined first as a field and then as a variable local to the run() method. there's nothing wrong with that and you can access both the variable and the field.
but once you define a local variable, you cannot create duplicates.
edit --
had to add this before anyone gets me wrong: the translation is not exactly like this (thus the "roughly"). Instead of a field you add a value to the binding of the script, quite like args for commandline scripts or request, session or response for groovlets.
but that is much a longer story...
ok if you really want to know just ask again and i'll explain it better
edit 2 --
i just can't leave it like this, if you ever need more info...
every groovy script has a field named binding, an instance of groovy.lang.Binding or one of its a subclasses.
this binding is basically a map, with methods setVariable and setVariable.
when you omit the def keyword when assigning a value in a script you are actually calling the method setVariable, and when you do something like this.x you are calling the getVariable method.
this is actually because class groovy.lang.Script overrides the methods getProperty and setProperty to call those methods first. that's the reason they behave like fields.
you might have also noticed that there is no type associated to those variables... that's because we are dealing with just a Map inside the binding.
standard groovy scrips are created with an instance of a binding with the args set to the array of parameters.
others, like groovy.servlet.ServletBinding define more variables and behavior, like block the assignment of certain variables, or adding a lazy initialization capabilities...
then the real reason behind the error is... if the def keyword is not used, fruits is not a real variable. still, i believe the behavior is somewhat analog to a field.
sorry about all that.
i was not satisfied with my own oversimplification :S
That String fruit shouldn't be having the same name as your def fruit. (you are defining first a list and then a string with the same name)
def likeIt = { String fruit -> println "I like " + fruit + "s" }
In the second case you are defining the type of the variable with def a posteriori, so it works but it is not a good practice as far as I know.
I think that you don't even need to write ->. The groovy manual says that "The -> token is optional and may be omitted if your Closure definition takes fewer than two parameters", which is the case here.
Second line
String fruit
the same variable name 'fruit' is being used again
I don't understand why the following code doesn't compile:
class Abc
{
def b (x : String) = x + "abc"
def a (y : String) =
{
val ls : List[String] = y.lines toList
b (ls.head)
}
}
Main.scala:8: error: type mismatch;
found : java.lang.String
required: Int
b (ls.head)
When I change "y.lines toList" to
y.lines.toList
or even to
y.lines toList;
it does compile.
Perhaps the compiler understands it like
(y.lines).toList(b (ls.head))
or something like that, but I still don't understand the rules.
It's not obvious, and it's a combination of Scala's shortcut syntax and list indexing. If you want a hint, try redefining b to:
def b(x : String) = 0
You'll get some other compiler garbage back, but the error will change. In short, the Scala compiler will let you omit parens and dots for zero- or one-parameter methods, and we know b looks like it's somehow getting chained.. The rub is that Scala also uses parens for list indexing, so toList, which returns an iterator, may take one parameter as the list index. I'm not sure of this part exactly, but it looks like once you start omitting dots, the lexer will become greedy, and when it comes across a method that may take one parameter, will attempt to pass the next statement to it. In this case, that's a string, so it throws a syntax error.
You got it spot on with this:
(y.lines).toList(b (ls.head))
With the only possible correction being:
(y.lines).toList(b).apply(ls.head)
I'm not sure that Scala would decide in this particular case.
The rule, roughly speaking, is object (method parameters)* [method]. The compiler will continue as long as it finds tokens for a valid expression. A ; finishes the expression, and so would a ) or }. If the next line is blank, the expression also ends. If the next line begins with a reserved keyword (val, def, if, etc), the expression would end too.