В Scala.js , как бы вы навигационно перемещались по графам объектов?

#scala #path #scala.js #scala-macros #scala-reflect

#scala #path #scala.js #scala-макросы #scala-reflect

Вопрос:

Предположим, что вы хотите перемещаться по графу объектов навигационным способом, аналогичным тому, как мы перемещаемся по файловым системам.

Например, представьте, что у вас есть граф объектов, который поддерживает это выражение:

var x = objName.foo.bar.baz.fieldName

Мы можем закодировать это выражение доступа к данным как path следующим образом:

 "objName/foo/bar/baz/fieldName"
  

Разбивая этот путь на сегменты, мы можем легко перемещаться по графу объектов в JavaScript, потому что в дополнение к традиционной точечной нотации он также поддерживает нотацию доступа к массиву: objName["foo"]["bar"]["baz"]["fieldName"] .

В Java или JVM Scala мы можем использовать отражение для перемещения по графам объектов, но как бы вы следовали этим типам путей для перемещения по графам объектов Scala в Scala.js среда?

Другими словами, учитывая путь, похожий по форме на URI, как бы вы прошли по Scala.js объекты и поля?

Комментарии:

1. Объекты, определенные в Scala, или объекты, определенные в JS? Я думаю, что в настоящее время вы получаете разные ответы, в зависимости. (Хотя я думаю, что в Scala 3 это может работать одинаково в любом случае.) Честно говоря, я не уверен, как это сделать для обычных объектов Scala в Scala.js окружающая среда…

2. Привет, Джастин. Речь идет об обычных объектах Scala в Scala.js среда, но если она не может работать для объектов scala в целом, можете ли вы придумать способ разработки классов Scala с учетом этой возможности?

Ответ №1:

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

Если цель состоит в том, чтобы иметь возможность обходить частично построенные пути, то это всего лишь вопрос передачи функций доступа. Вы могли бы использовать что-то подобное, чтобы сделать его красивее:

 class Path[ A](private val value: () => A) {
  def resolve: A = value()
  def /[B](f: A => B): Path[B] = new Path(() => f(resolve))
}

implicit class PathSyntax[A](val a: A) extends AnyVal {
  def /[B](f: A => B): Path[B] = new Path(() => a) / f
}


object Foo {
  val bar = Bar
}

object Bar {
  val baz = Baz
}

object Baz
  

Синтаксис не совсем такой красивый, но теперь он безопасен для ввода и выглядит не так уж плохо:

 val barPath: Path[Bar.type] = Foo / (_.bar)
val bazPath: Path[Baz.type] = barPath / (_.baz)
  

Против этого потребовалось бы больше работы, например. между ними нет надлежащего равенства / сравнения Paths .

Если вы хотите придерживаться своего подхода, я не знаю прямого решения. Однако я бы сказал, что весь смысл использования ScalaJS заключается в сохранении строгих типов и избежании любого шаблона, который мог бы привести к ошибкам во время выполнения, которые компилятор мог бы предотвратить, если бы вы позволили ему выполнять свою работу.