Могу ли я использовать динамический Scala для программного создания экземпляра объекта какой-либо черты? - PullRequest
0 голосов
/ 31 августа 2018

У меня есть список сигнатур методов некоторых trait T, представленных в виде объектов, аналогично следующему, игнорируя арность:

case class Method[T,A,B](name: String, f: T => A => B)

Итак, учитывая некоторую черту T, это может выглядеть так:

trait T{
  def foo(i: Int): String
  def bar(s: String): Unit
}

def methodsOfT: Seq[Method[T,_,_]] = Seq(
  Method("foo", t => i => t.foo(i)),
  Method("bar", t => s => t.bar(s))
)

Я хочу создать экземпляр типа T или класса, связанного с T, независимо от того, что использует словарь объектов Method для реализации интерфейса T.

Это может выглядеть следующим образом (это не скомпилируется):

def reify[T](impl: T, methods: Method[T,_,_]): T = (new Object with Dynamic {
  def applyDynamic(method: String)(args: Any*): Any = {
    //do something here
  }
}).asInstanceOf[T]  //<- will fail at runtime

Конечно, возвращаемый объект имеет тип Object и не может быть приведен к типу / классу T. Есть ли способ обойти эту проблему?

Почему я хочу это сделать

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

new T {
  def foo(i: Int): String = 
    methodsOfT.find(_.name == "foo").get.f(i).asInstanceOf[String]
  def bar(s: String): Unit = 
    methodsOfT.find(_.name == "bar").get.f(s).asInstanceOf[Unit]
}

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

приписка

Следующий вопрос очень связан. В настоящее время я изучаю этот подход: Отражение Java: создание реализующего класса

1 Ответ

0 голосов
/ 31 августа 2018

Я полагаю, что подобное делается в бесформенных линзах.

Здесь вы можете видеть, что динамика делегирует создание объектива неявному - возвращаемый тип берется из функциональных зависимостей (здесь: тот mkLens.Out)

trait Lens[S, A] extends LPLens[S, A] { outer =>
...
  def selectDynamic(k: String)
    (implicit mkLens: MkSelectDynamicOptic[Lens[S, A], A, Symbol @@ k.type, Nothing]): mkLens.Out = mkLens(this)
...
}

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

object Generic1 extends Generic10 {
...

  implicit def mkGeneric10[T[_], U[_], FR[_[_], _[_]]]: Generic1[T, ({ type λ[t[_]] = FR[t, U] })#λ] =
    macro Generic1Macros.mkGeneric1Impl[T, ({ type λ[t[_]] = FR[t, U] })#λ]

...
}

Комбинация макро + динамика используется в HList .

object HList extends Dynamic {
...
  /**
   * Allows to specify an `HList` type with a syntax similar to `Record` and `Union`, as follows,
   *
   * {{{
   * type ISB = HList.`Int, String, Boolean`.T
   * }}}
   *
   * Literal types are allowed, so that the following is valid,
   *
   * {{{
   * type ABC = HList.`'a, 'b, 'c`.T
   * type TwoTrueStr = HList.`2, true, "str"`.T
   * }}}
   */
  def selectDynamic(tpeSelector: String): Any = macro LabelledMacros.hlistTypeImpl
...
}

В общем, вы должны быть в состоянии достичь своей цели. Все зависит только от того, как именно вы хотите создать экземпляр своего объекта - например, Возьмите неявный ClassTag и вызовите конструктор, используя отражение или, возможно, построив HList и используя Generic, чтобы превратить его в конкретное представление или, может быть, что-то еще.

...