Можно ли (повторно) связать приемник внутри блока кода?

Проблема

Этот вопрос мотивирован попыткой найти решение для этого вопроса.

Предположим, вы хотите построить иерархическую структуру, используя следующий синтаксис:

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

Строительный DSL должен быть типобезопасным, то есть не должно быть возможности вложить subB непосредственно в root или вложить subA в другой subA. Следовательно, моя идея состоит в том, чтобы иметь метод root, возвращающий объект, определяющий метод subA, где последний, в свою очередь, возвращает объект, определяющий subB.

Теперь я хотел бы, чтобы блок кода передавался root, то есть

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

выполняется таким образом, что вызов subB привязан к объекту, созданному root. В основном так

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

но без необходимости явно указывать получателей r и sa.

Вопрос. Возможна ли повторная привязка приемников, особенно неявного this-приемника, внутри блока кода в Scala — возможно, с помощью макросов?

Другие подходы

В этой статье описывается похожее построение DSL для XML-деревьев. Их реализация основана на функции Dynamic, и результирующий синтаксис DSL выглядит следующим образом:

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

Однако этот подход требует явных приемников (здесь объект xml), и, что еще более серьезно, я не думаю, что он безопасен для типов в том смысле, что он статически не позволит вам вложить узел html в узел title.


person Malte Schwerhoff    schedule 31.01.2013    source источник


Ответы (1)


Я не уверен, что это можно использовать, но он очень похож на код, который вы написали. Я добавил , и изменил { и } на ( и ).

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")
  )
)

Я не уверен, как вы хотите его использовать, я мог бы попытаться сделать более конкретную версию на основе варианта использования, если бы вы сделали это доступным.


Редактировать

Возможна ли повторная привязка приемников, особенно неявного this-receiver, внутри блока кода в Scala — возможно, с помощью макросов?

Нет, насколько я могу судить, это невозможно из коробки. Вы, вероятно, сможете сделать это с помощью макроса, но я не могу вам в этом помочь. Мои ограниченные знания макросов говорят мне, что это будет настоящим приключением.

Чтобы добиться такого же эффекта без макросов, я создал небольшой пример. Я удалил лень, чтобы сделать его более кратким.

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")
  )
)
person EECOLOR    schedule 16.02.2013
comment
Это более или менее то, что я придумал, см. мой ответ на вопрос, на который я ссылался в своем вопросе выше. С точки зрения синтаксиса это действительно близко к тому, что я описал здесь, но с точки зрения реализации, очевидно, это совершенно другое. Во всяком случае, это определенно лучший ответ на далеко :-) - person Malte Schwerhoff; 18.02.2013
comment
Не могли бы вы добавить к своему ответу пример окончательной структуры объекта, которую вы хотели бы получить? Затем я мог бы помочь вам понять, как перейти от этой структуры к другой. - person EECOLOR; 18.02.2013
comment
Как сказано (о чем свидетельствует мой ответ stackoverflow.com/a/14608594/491216), я знаю, как использовать ваш подход к добиться того, чего хочет ОП этого ответа на вопрос. Что меня интересует здесь, так это то, можно ли изменить получателя вызовов внутри блока кода/внутри (анонимной) функции. - person Malte Schwerhoff; 18.02.2013
comment
Извините, но ваше решение просто (более или менее) повторяет то, что я уже опубликовал в качестве ответа на этот другой вопрос. Если вы считаете иначе, укажите, чем ваше решение отличается от моего. - person Malte Schwerhoff; 18.02.2013
comment
Я понимаю, что вы имеете ввиду. Не могли бы вы уточнить, чего вы хотите достичь? У меня такое ощущение, что «перепривязка приемников» - это не решение того, чего вы хотите достичь. Обновленный ответ отражает ваш вопрос, поскольку subA теперь выполняется как (r:Root).subA. - person EECOLOR; 18.02.2013
comment
давайте продолжим это обсуждение в чате - person Malte Schwerhoff; 18.02.2013