scala - 如何将包含参数的案例类与通用类型匹配
问题描述
我有一个有趣的问题,匹配 Scala 中的案例类......
我正在使用 Akka,并且我拥有将在系统中的每个 Actor 中使用的功能,因此为我的 Actor 创建了一个基类,我尝试在那里匹配该命令....
我的命令如下所示...
sealed trait ReportCommand extends ProcessCommand
final case class onReport(key: Key, replyTo: ActorRef[ResponseBase[State]]) extend ReportCommand
虽然我构造了 Base Class 以便它可以从不同的 Actor 中使用,但onReport
作为通用参数传递给 Base Actor,以用于与案例类的模式匹配......
abstract class BaseActor[E: ClassTag, R <: ReportBase[STATE], COMMAND](signal: TypeCase[R]) {
private val report = signal
def base[B <: E: ClassTag](cmd: E, state: STATE)(f: B => ReplyEffect[COMMAND, STATE]): ReplyEffect[COMMAND, STATE] =
cmd match {
case report(report) =>
Effect.reply(report.replytTo)(new ResponseBase[STATE]{
override def state: STATE = state
})
}
}
首先,如果您认为这个构造不起作用,它会起作用,我有另一个命令(我没有放在这里),它在命令类中没有通用参数,上面的代码片段能够匹配那个片段。
现在,当我第一次尝试此代码时,Shapeless 抱怨 TypeCase 的 Typeable 没有到 ActorRef 的映射,所以在研究了互联网后,我发现我必须执行以下操作....
implicit def mapActorRef[T: ClassTag]: Typeable[ActorRef[T]] =
new Typeable[ActorRef[T]] {
private val typT = Typeable[T]
override def cast(t: Any) : Option[ActorRef[T]] = {
if(t==null) None
else if(t.isInstanceOf[ActorRef[_]]) {
val o= t.asInstanceOf[ActorRef[_]]
for {
_ <- typT.cast(myClassOf)
} yield o.asInstanceOf[ActorRef[T]]
} else None
}
}
def myClassOf[T: ClassTag] = implicitly[ClassTag[T]].runtimeClass
implicit def responseBaseIsTypeable[S: Typeable] : Typeable[ResponseBase[S]] =
new Typeable[ResponseBase[S]] {
private val typS = Typeable[S]
override def cast(t: Any) : Option[ResponseState[S]] = {
if(t==null) None
else if(t.isIntanceOf[ResponseBase[_]]) {
val o = t.asInstanceOf[ResponseBase[_]]
for {
_ <- typS.cast(o.state)
} yield o.asInstanceOf[ResponseBase[S]]
} else None
}
}
现在,在进行此更改后,我没有收到来自 Shapeless 的任何异常但case report(report)
不匹配,我不知道我们如何从 Scala 那里得到推理,为什么它决定它不匹配。在我的调试会话中,我观察到以下内容。
我正在使用 Akka 的 Ask Pattern 与这个演员交流......
val future : Future[BaseActor.ResponseBase[Actor.State]] = actorRef.ask[BaseActor.ResponseBase[Actor.State]](ref =>
Actor.onReport(key, ref)
)
现在,如果我观察 BaseActor 收到的 cmd 对象,我看到 Akka 的“询问”模式将onReport
Command 类中的 ActorRef 更改为 ActorRefAdapter,当然 ActorRefAdapter 是 ActorRef 的子类,但我不确定我在将 ActorRef 映射到 TypeCase 的隐式可以处理这些东西,但我想不出一种方法来更改隐式以了解子类型......
不幸的是,ActorRefAdapter 是私有的,package akka.actor.typed.internal.adapter
所以我不能为 ActorRefAdapter 定义额外的映射。
所以任何人都可以看到为什么 Scala 不匹配我的 Shapeless <-> TypeCase 配置并给我一些提示......
谢谢回答...
解决方案
您的实例Typeable[ActorRef[T]]
不正确。
你为什么决定替换 a ClassTag
in typT.cast(myClassOf)
?这不可能有意义。
我猜你使用 Shapeless 2.1.0-RC2 使用了类似“参数化类型没有默认类型”之类的东西
如果您的目标是进行case report(replyTo)
匹配,那么您可以定义
implicit def mapActorRef[T: Typeable]: Typeable[ActorRef[T]] =
new Typeable[ActorRef[T]] {
private val typT = Typeable[T]
override def cast(t: Any): Option[ActorRef[T]] = {
if (t == null) None
else util.Try(t.asInstanceOf[ActorRef[T]]).toOption
}
override def describe: String = s"ActorRef[${typT.describe}]"
}
问题是这个实例也很糟糕。现在case report(replyTo)
匹配太多了。
val actorTestKit = ActorTestKit()
val replyToRef = actorTestKit.spawn(ReplyToActor(), "replyTo")
import BaseActor._ // importing implicits
import shapeless.syntax.typeable._
val future: Future[BaseActor.ResponseBase[Actor.State]] = replyToRef.cast[ActorRef[Int]].get.ask[BaseActor.ResponseBase[Actor.State]](ref =>
1
)(5.seconds, system.scheduler)
Await.result(future, 10.seconds) // ClassCastException
类型类的合法实例Typeable
不能为每种类型定义。
为多态类型(定义明确)提供实例(具体实例化)几乎是
Typeable
这里和 Haskell 中的全部要点。上面的关键词是“定义明确的地方”。在类似非空容器的情况下,它的定义很好。对于函数值,它显然没有很好地定义。
https://github.com/milessabin/shapeless/issues/69
ResponseBase
是一个类似非空容器的东西。butActorRef
就像一个函数T => Unit
,所以不应该有Typeable
for 它
trait ActorRef[-T] extends ... {
def tell(msg: T): Unit
...
}
你应该重新考虑你的方法。
推荐阅读
- python-3.x - 从抓取的文本中提取非结构化地址和电子邮件 ID 作为变量 - Python
- reactjs - 使用 setState Hook 根据 React JS 中的一组条件更新先前的值
- flutter - 无法在真实设备上调试 Flutter 应用程序在模拟器上运行良好
- spring-boot - 我的休息控制器没有从 URL 调用
- docker - 复制失败 '/target/x86_64-unknown-linux-musl/release/microservice_app: no such file or directory' Rust Dockerfile
- node.js - Socket.io Node Js 服务器和 React js 客户端未连接
- java - Android Volley:BasicNetwork.performRequest:意外响应代码 400
- node.js - 代理错误:无法将请求 /user/update 从 localhost:3000 代理到 http://localhost:5000
- python - 如何在 Tkinter 中将类/窗口从“普通”窗口转换为 Toplevel() 窗口?
- python - Python 缓存工具在多核服务器中共享内存