akka 如何将包含参数的案例类与泛型类型进行匹配

8yparm6h  于 2022-11-05  发布在  其他
关注(0)|答案(1)|浏览(124)

我在Scala中遇到了一个匹配Case类的有趣问题......
我正在使用Akka,我的系统中的每个Actor都将使用我的功能,因此我为Actor创建了一个基类,并尝试匹配该命令......
我的命令如下所示...

sealed trait ReportCommand extends ProcessCommand
final case class onReport(key: Key, replyTo: ActorRef[ResponseBase[State]]) extend ReportCommand

虽然我构造了基类,以便可以从不同的参与者使用它,但是onReport作为泛型参数传递给了Base Actor,以便在与case类进行模式匹配时使用...

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没有Map到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 的询问模式与此演员进行交流...

val future : Future[BaseActor.ResponseBase[Actor.State]] = actorRef.ask[BaseActor.ResponseBase[Actor.State]](ref =>
   Actor.onReport(key, ref)
)

现在,如果我观察BaseActor接收的cmd对象,我会看到Akka的“ask”模式将onReport Command类中的ActorRef更改为ActorRefAdapter,当然,ActorRefAdapter是ActorRef的子类,但我不确定我在隐式中定义的将ActorRefMap到TypeCase的内容是否可以处理这些内容,但我无法找到一种方法来更改隐式以了解子类型......
不幸的是,ActorRefAdapter对于包package akka.actor.typed.internal.adapter是私有的,所以我不能为ActorRefAdapter定义额外的Map。
所以,有人能理解为什么Scala不能匹配我的Shapeless <->TypeCase配置,并给予我一些提示吗?
谢谢你的回答。

xxe27gdn

xxe27gdn1#

示例Typeable[ActorRef[T]]不正确。
为什么你决定用ClassTag代替typT.cast(myClassOf)?这不可能有意义。
我猜你用了"No default Typeable for parametrized type" using 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是一个 * 非空的类似容器的东西 *,但是ActorRef就像一个函数T => Unit,所以不应该有一个Typeable

trait ActorRef[-T] extends ... {
  def tell(msg: T): Unit

  ...
}

你应该重新考虑你的方法。

相关问题