Время выполнения, избегая asInstanceOf - PullRequest
0 голосов
/ 30 сентября 2019

Вот упрощенная версия проекта, который пытается создать функцию во время выполнения. Таким образом, pipelineS приходит во время выполнения (в форме json или чего-то еще), и траверс выполняет его. Кроме того, мы знаем, что входные данные во время выполнения будут корректной композицией (совпадение типов входных / выходных функций). Я хочу определить функции, которые имеют информацию о типе и избегают asInstanceOF. Вот частичное решение, которое я вижу: Здесь , но оно очень сложное и его сложно расширить до входных аргументов другого размера.

abstract class Func extends Product {
  val meta:FuncMeta
}

case class FuncMeta(name:String, outType:String, inTypes:List[String])
case class Fun0(meta:FuncMeta, run:() => Any) extends Func
case class Fun1(meta:FuncMeta, run:Any => Any) extends Func
case class Fun2(meta:FuncMeta, run:(Any, Any) => Any) extends Func

val literal2 = Fun0(FuncMeta("literal2", "int", List.empty), () => 2)
val literal10 = Fun0(FuncMeta("literal10", "int", List.empty), () => 10)
val twice = Fun1(FuncMeta("twice", "int", "int" :: Nil) ,(a:Any) => a.asInstanceOf[Int] * 2)
val larger = Fun2(FuncMeta("larger", "bool", "int" :: "int" :: Nil) ,(a:Any, b:Any) => a.asInstanceOf[Int] > b.asInstanceOf[Int])
val add = Fun2(FuncMeta("add", "int", "int" :: Nil), (a:Any, b:Any) => a.asInstanceOf[Int] + b.asInstanceOf[Int])

//a Map[String, Func] for runtime access and retreave of functions
//Basically this is the way the Functions are stored
val funcs = List(literal2, literal10, twice, larger, add).map(x => x.meta.name -> x).toMap

def traverse(tree: Treee[Func]):Any = {
  val t = tree.t
  val kids = tree.kids

  val rs = kids.map(k => traverse(k))
  t match {
    case Fun0(meta, run) => run()
    case Fun1(meta, run) => run(rs.head)
    case Fun2(meta, run) => run(rs(0), rs(1))
  }
}

//RUNTIME information
//can be a userinput Json that get's converted to the following by accessing the funcs Map
val pipelineS = Treee[Func](
    funcs("larger"),
    List(
        Treee(funcs("literal10")),
        Treee(funcs("literal2"))
      )
  )

println(traverse(pipelineS))

Ответы [ 2 ]

1 голос
/ 01 октября 2019

Ну, вы можете создать типизированную кодировку, например, такую:

sealed trait Term {
  type T
  def run(): T
}

object Term {
  type Aux[_T] = Term { type T = _T }
}

final case class Lit[A] (value: A) extends Term {
  override final type T = A
  override final def run(): T = value
}

final case class Tuple[A, B](a: Term.Aux[A], b: Term.Aux[B]) extends Term {
  override final type T = (A, B)
  override final def run(): T = (a.run(), b.run())
}

final case class Fun[I, O](input: Term.Aux[I])(f: I => O) extends Term {
  override final type T = O
  override final def run(): T = f(input.run())
}

, которую можно использовать так:

val expr: Term = Fun(Tuple(Lit(2), Fun(Lit(3))(_ * 5))) {
  case (a, b) => a + b
}

expr.run()
// res: Term.T = 17

Проблема заключается в том, как создать такое выражение изваш пользовательский ввод.

0 голосов
/ 01 октября 2019

Не думайте, что есть простые решения, если вы хотите избежать приведения типов во время выполнения функции.

Если бы мне действительно пришлось это сделать, я бы начал так:

1) Начните с функций одного аргумента. FunctionMeta[I, O] должен быть набран с типом ввода и типом вывода. Приведение типов будет происходить, когда вы анализируете свой json в FunctionMeta[I, O], а не при его запуске.

2) Используйте структуры данных с выравниванием типов, чтобы сохранить последовательность функций, как предложено в обсуждении, которое вы связали.

3) После того, как вы заработаете функции с 1 аргументом - моделируйте функции нескольких аргументов как функции из HList.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...