2

Problem

This question is motivated by trying to find a solution for this question.

Assume that you would like to construct a hierarchical structure by using the following syntax:

root {
  subA {
    subB("b1.1")
    subB("b1.2")
  }
}

The construction DSL should be type-safe, that is, it should not be possible to nest a subB directly in root, or to nest a subA in another subA. Hence, my idea is to have a method root that returns an object defining a method subA, where the latter in turn returns an object defining subB.

What I would now like to have is that the block of code passed to root, that is,

  subA {
    subB("b1.1")
    subB("b1.2")
  }

is executed such that the invocation of subB is bound to the object created by root. Basically like this

root { r: Root =>
  r.subA { sa: SubA =>
    sa.subB("b1.1")
    sa.subB("b1.2")
  }
}

but without having to make the receivers r and sa explicit.

Question: Is rebinding receivers, especially the implicit this-receiver, inside a block of code possible in Scala - maybe using macros?

Other approaches

This article describes a similarly-looking construction DSL for XML trees. Their implementation is based on the Dynamic feature and the resulting DSL syntax looks like this:

xml html {
  xml head {
    xml title "Search Links"
  }
}

However, this approach requires explicit receivers (here the object xml), and more severely, I don't think that it is type-safe in the sense that it would statically prevent you from nesting an html node inside a title node.

Community
  • 1
  • 1
Malte Schwerhoff
  • 12,684
  • 4
  • 41
  • 71

1 Answers1

0

I am not sure if this is usable, but it looks very similar to the code you have written. I have add , and changed the { and } into ( and ).

trait RootElement
trait SubAElement

def root(f:(() => RootElement)*) = ???
def subA(f:(() => SubAElement)*):() => RootElement = ???
def subB(s:String):() => SubAElement = ???

root(
  subA (
    subB("b1.1"),
    subB("b1.2")
  )
)

I am not sure how you want to use it, I could try and make a more specific version based on the use case if you made that available.


Edit

Is rebinding receivers, especially the implicit this-receiver, inside a block of code possible in Scala - maybe using macros?

No, as far as I can tell, that's not possible out-of-the box. You probably will be able to do it with macro's, but I can not help you with that. My limited knowledge of macro's tells me that will be quite an adventure.

To achieve the same effect without macro's I created a small example. I have removed the laziness to make it more concise.

class Root {
  def subA(subA: SubA) = {
    // do something with subA
  }
}

class SubA {
  def subB(s: String) = {
    // do something with subB
  }
}

case class RootElement(value: SubA)
case class SubAElement(value: String)

def root(rootElems: (RootElement)*): Root = {
  val r = new Root

  rootElems foreach { sub =>
    r.subA(sub.value)
  }
  r
}

def subA(subElems: SubAElement*): RootElement = {
  val sA = new SubA
  subElems foreach { sub =>
    sA.subB(sub.value)
  }
  RootElement(sA)
}

def subB(s: String): SubAElement = SubAElement(s)

root(
  subA(
    subB("b1.1"),
    subB("b1.2")
  )
)
EECOLOR
  • 11,184
  • 3
  • 41
  • 75
  • That's more or less what I came up with as well, see my answer to the question to which I referred in my question above. Syntax-wise it is indeed close to what I described here, but implementation-wise it is obviously totally different. Anyway, this is definitely the best answer to far :-) – Malte Schwerhoff Feb 18 '13 at 08:51
  • Could you add to your answer an example of the final object structure you would want? I could then help you figure out how to go from this structure to the other one. – EECOLOR Feb 18 '13 at 09:43
  • As said (and witnessed by my answer http://stackoverflow.com/a/14608594/491216) I know how to use your approach to achieve what the OP of that answered question wants. What I am interested in here is whether it is possible to change the receiver of calls inside a block of code/inside a (anonymous) function. – Malte Schwerhoff Feb 18 '13 at 09:47
  • I am sorry, but your solution just (more or less) repeats what I had already posted as an answer to that other question. If you think otherwise, please point out what differentiates your solution from mine. – Malte Schwerhoff Feb 18 '13 at 11:52
  • I see what you mean. Could you try to make it more clear what you want to achieve? I get a feeling that 'rebinding receivers' is not the solution for what you want to achieve. The updated answer reflects your question as `subA` is now executed as `(r:Root).subA`. – EECOLOR Feb 18 '13 at 12:26
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/24704/discussion-between-mhs-and-eecolor) – Malte Schwerhoff Feb 18 '13 at 12:38