在Scala中,以字符串到案例类的映射以及以这些案例类作为输入参数的从字符串到函数的映射的类型应该是什么?

马赫迪

我尝试建模的场景如下。我有几个case类,它们的参数不同,但是它们都扩展了trait Entity

// case classes 
trait Entity
case class E1(..params..) extends Entity
case class E2(..params..) extends Entity
...
case class En(..params..) extends Entity

我有一组带有一个参数的函数,该参数是的子类型Entity,如下所示(我们有比实体更多的功能):

// functions using case classes as parameters
def f1(val p:E1) = ???
def f2(val p:E4) = ???
...
def fm(val p:E2) = ??? 

现在,我得到了一个Entity序列化为String的实例,在它旁边,我得到了要调用它的函数的名称。反序列化不是问题:假设我有一个read[T](str)可以反序列str化为type对象的函数T

我想编写一个通用的Scala代码,鉴于这两个字符串(函数名称,序列化实体),可以在反序列化实体后调用正确的函数。

我以为,我需要像下面这样的映射,给定一个函数名称将给我函数本身及其参数的类型。然后,原则上我应该可以轻松地拨打如下电话。

// the mappings from String to entity and corresponding function
val map1 = Map (
    "f1" -> f1
    "f2" -> f2
    ...
    "fn" -> fn
  ) 
}
val map2 = Map (
    "f1" -> E1
    "f2" -> E4
    ...
    "fn" -> E2
  ) 
}

def makeTheCall (fname: String, ent: String) = map1.get(fname)(read[map2.get(fname)](ent))
  1. 这是行不通的,因为我无法正确选择类型(而且推断的类型也无法正常工作)。

  2. 有没有把办法map1map2在一起(这样有少搞乱的功能和参数类型之间的关系的机会)?


编辑:为简单起见,我们在这里可以忽略实体的参数,因此也可以忽略实际的序列化实体。这应该有助于编写无需太多工作的可编译代码。


编辑:用例:我正在编写一个从RabbitMQ接收消息的程序。消息的主体包含该实体,并且消息密钥暗含了对该实体的处理方式。

彼得·内恩斯(Peter Neyens)

编辑2:使用与我先前的编辑功能类似的功能,可以创建一个地图,String => Unit地图的功能将反序列化和您的函数结合在一起,因此您不需要两个地图。

def deserializeAnd[E <: Entity](f: E => Unit): String => Unit = 
  (s: String) => f(read[E](s))

val behaviour = Map(
  "key1" -> deserializeAnd(println(_: Foo)),
  "key2" -> deserializeAnd(println(_: Bar)),
  "key3" -> deserializeAnd((foo: Foo) => println(foo.copy(a=0))
)

def processMessage(key: String, serialized: String): Option[Unit] = 
  behaviour.get(key).map(f => f(serialized))

// throws an exception if 'behaviour' doesn't contain the key
def processMessage2(key: String, serialized: String): Unit =
  behaviour(key)(serialized)

编辑:似乎您可能具有相同输入类型的多个功能,这对于类型类而言不是一个好用例。

您可以使用类似:

def makeTheCall[E <: Entity, Out](f: E => Out, s: String): Out = f(read[E](s))

它将字符串反序列化为所传递函数的输入类型。
您可以将其用作makeTheCall(f2, "serializedE4")


即使可以找到合适的类型来使makeTheCall方法正常工作,也不应使用字符串来区分多个类型。如果您在fnamemap1包含fnamemap2包含...中打错了怎么办?

从您的问题中并不清楚您要确切执行什么操作,但似乎类型类将非常适合您的情况。与类型类,你可以创建一个类型的,像你想与你做哪些可以做一些特定功能的情况下f1f2...等功能。

假设您的f1,,f2...函数都返回Int,我们可以创建一个类型类,其中包含此类用于Entity类型的函数

trait EntityOperation[E <: Entity] {
  def func(e: E): Int 
}

让我们创建一些扩展的case类Entity

trait Entity
case class Foo(a: Int, b: Int) extends Entity
case class Bar(c: String, d: String) extends Entity

现在,我们可以为Foo创建类型类的实例Bar

implicit val FooEntityOp = new EntityOperation[Foo] {
  def func(foo: Foo) : Int = foo.a + foo.b
}

implicit val BarEntityOp = new EntityOperation[Bar] {
  def func(bar: Bar) : Int = bar.c.length + bar.d.length
}

我们可以如下使用我们的类型类:

def callF[E <: Entity](e: E)(implicit op: EntityOperation[E]) = op.func(e)

callF(Foo(1, 2))          // Int = 3
callF(Bar("xx", "yyyy"))  // Int = 6

在您的情况下,可能看起来像:

def makeTheCall[E <: Entity](s: String)(implicit op: EntityOperation[E]) = 
  op.func(read[E](s))

// makeTheCall[Baz]("serializedBaz")

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

Related 相关文章

热门标签

归档