4

My question might be contrived, and I would like to consider it more than a proof of feasibility than something recommended. To give you some context, I would try to have in an embedded DSL a different syntax than

val myVal = "someContent"

I would like to be able to do something like that:

assign("SomeContent","someVariable") => converted to
someVariable = "SomeContent"

and then later in the code, the variable is available. Typically we could have something like in the REPL:

assign("John","name")
println("Hello " + name)

I have been thinking that macros (or ScalaCompiler plugin but I think that here it is even more complicated) in order to do the trick. First I don't know if this is feasible being new to macros.

I have started with something simplistic considering that I would only manipulate String and I started with something like

def assign(content: String, targetVal: String):Unit = macro assignMacro

def assignMacro(c:Context)(content: c.Expr[String], 
                           targetVal: c.Expr[String]):c.expr[Unit] = {
  import c.universe._

  c.Expr[Unit](ValDef(Modifiers(), TermName(targetVal.value),
                      TypeTree(), Literal(Constant(content.value)))
}

Unfortunately it seems to fail due to several mistakes

  • First it complains when I try to create a new term name suggesting if I am sure then I should call eval of my expression. Unfortunately I am not sure ;) and if I try, it fails ;)
  • If I replace this targetVal and content with constants like myVal and myContent, I get a second error message like compiler found and required Unit

I am a bit stuck. First is this possible ? I would guess yes ;) And how could I achieve this ?

Thanks for the help

Best Regards

pascal
  • 87
  • 6
  • This is not possible in this form in 2.10, but you could write something like `val syntax = assign("John", "name"); import syntax._` to get the same effect. – Travis Brown Sep 17 '13 at 10:54
  • I am using 2.11.0-M4. Is it possible ? – pascal Sep 17 '13 at 11:00
  • I don't think so, but I'm not familiar with all the new corners of 2.11 yet. – Travis Brown Sep 17 '13 at 14:30
  • If the variable name to assign is not available at compile time, how would the macro go about generating the assignment? (it's equally likely though that I'm missing something) – Erik Kaplun Sep 17 '13 at 14:56
  • In fact the variable name has to be provided by the user. Initially I did something like val variableName = ... and it works fine obviously. I was then challenged if I could have something like assign(SomeContent).to(someVar). Honestly this starts to be a bit weird. I mainly would like to know if this is possible to use macros or whatever in order to achieve this while remaining inside Scala (I would like to avoid Parser Combinators). – pascal Sep 17 '13 at 15:03

1 Answers1

3

Both in 2.10 and in 2.11 def macros expand into blocks, which means that definitions they introduce become local to that block. This is likely to change in macro paradise, but I'm not sure when and how.

At the moment macros in Scala don't provide ways to transparently introduce new variables into existing scopes. This is a historical artifact of evolution of Scala macros, caused by fact that our reflection group prioritized blackbox macros (macros that behave like regular methods, completely described by their type signatures, so that humans and programs can treat their implementations as black boxes without missing anything out).

Recent interest in whitebox macros (macros that don't fit into the blackbox scheme) has yielded some results, but these results haven't yet been included in mainline Scala. I'll be elaborating more on this matter in my tomorrow's talk at Strange Loop: https://thestrangeloop.com/sessions/evolution-of-scala-macros.


In the meanwhile you could take a look at macro annotations that might provide the degree of freedom that you're looking for. For example, by appropriately defining a mydsl annotation, you could have

@mydsl 
def foo = {
  assign("SomeContent", "someVariable")
  ...
}

Transformed into

def foo = {
  someVariable = "SomeContent"
  ...
}
Eugene Burmako
  • 13,028
  • 1
  • 46
  • 59
  • This is what I fear when I tried Travis proposal. I got variables inside the block scope. Anyway this also makes sense because creating variables like that sounds to me like it breaks some principles of Scala - like creating side effects. Honestly I just gave a try and writing val name = ... is fine to me. Starting to really alter the code is a warning and in that case, an External DSL would be more appropriate. Thanks for the answer. I'll follow the evolutions – pascal Sep 18 '13 at 07:57
  • And I will look at the macro annotation suggestion. I think it might be an interesting option – pascal Sep 18 '13 at 07:59