5

In response to this question, I've been having a go at implementing a Haskell-style 'where' expression in Scala using the macro-paradise branch. The code is available at scala-where. I can now write something like the following:

val result = where ( f1(1) * f2(2), {
  def f1(x : Int) = x + 1
  def f2(x : Int) = x + 2
})

However, what I'd really like to do is to be able to call this in infix position:

val result = ( f1(1) * f2(2)) where {
  def f1(x : Int) = x + 1
  def f2(x : Int) = x + 2
}

Normally, this sort of thing would be easy, but I can't see how to do it with the macro call. The expression (f1(1) * f2(2)) won't type before macro application, so something like building an implicit value class doesn't work. Is there a way to get this kind of syntax otherwise?

Failing this, just having two parameter lists so one could do:

val result = where (f1(1) * f2(2)) {
  def f1(x : Int) = x + 1
  def f2(x : Int) = x + 2
}

would be nice, but again this seems difficult. Can one call a macro with two parameter lists?

Community
  • 1
  • 1
Impredicative
  • 5,039
  • 1
  • 17
  • 43

2 Answers2

2

For the first option: I would think you could make the implicit conversion an untyped macro itself, no?

For the second option: You can call a macro with multiple parameter lists, yes. Multiple lists at the call site will translate to multiple lists at the definition site, e.g.:

def myMacro(a: _)(b: _) = macro myMacro_impl

def myMacro_impl(c: Context)(a: c.Tree)(b: c.Tree): c.Tree = { ... }

Would be called as:

myMacro(...)(...)
Ptharien's Flame
  • 3,246
  • 20
  • 24
  • 1
    Two parameter list thing is great, thanks! I'd somehow missed that. – Impredicative Mar 07 '13 at 09:26
  • 1
    With regards to the implicit conversion - I'm not sure how to get the hook into the macro system. I don't think it can be an implicit conversion, since surely the tree must have been typed in order to search for conversions? – Impredicative Mar 07 '13 at 09:28
  • You may be right, although I would think you'd be able to do `implicit def conv = macro conv_impl; def conv_impl(c: Context)(x: c.Tree): c.Expr[T]` and have it be essentially of type `Any => T` – Ptharien's Flame Mar 07 '13 at 18:48
  • 1
    I think the problem is that the search for implicits is part of the typing phase, which is skipped when using an untyped macro, and it can't be typed until after macro execution. – Impredicative Mar 08 '13 at 10:20
  • 1
    I'm accepting this as answering the second part of my question, although the correct answer to the first part is given in my answer below (namely, it's not possible at the moment). – Impredicative Mar 08 '13 at 13:54
1

Answer: as of 2013-03-08 it is not possible to use untyped macros in an infix position. Quoted from Eugene Burmako on the scala-user mailing list:

Currently the argument on the left has to be typechecked first before any implicit resolution kicks in. The fact that you can write "class foo(x: _)" is an oversight - the underscore syntax is supposed to be working only in untyped macros.

For reference, the closest I came to being able to do this was the following:

implicit class HasWhere(val exp : _) {
 def where(block : Unit) = macro whereInfix
}

def whereInfix(c : Context)(block : c.Expr[Unit]) = {
  import c.universe._

  val exp = Select(c.prefix.tree, TermName("exp"))
  val Expr(Block((inner, _))) = block
  val newinner = inner :+ exp
  Block(newinner : _*)
}
Impredicative
  • 5,039
  • 1
  • 17
  • 43