scala - Scala 函数中的类型绑定使方法引用的管道复杂化
问题描述
我已经实现了一个简单的事件服务,它允许发布只读事件 ( Event
) 和可取消事件 ( PreEvent
)。所有处理程序都会减少可取消的事件,并将结果返回给调用者。
交互按预期工作,Event
但PreEvent
类型限制T <: PreEvent
给了我一些问题:
- (1) 当匹配 a 时,
PreEvent
它的副本必须显式转换T
为编译器才能实现它与方法参数的类型相同。 - (2) 当试图将 a
PreEvent
传递给方法引用时,编译器突然忘记它正在处理 aPreEvent
并尝试调用 的Event
变体publish
。
除了重命名EventService::publish(PreEvent)
方法以避免歧义之外,我是否可以对类型绑定进行任何更改Handler::reduce[T <: PreEvent](event: T): T
以帮助 Scala 实现在方法作为方法引用传递时(event: T)
始终是 a ?PreEvent
(因此没有可用的类型信息,尽管我希望编译器从上下文中找出这一点)
是否可以对类型绑定Handler::reduce[T <: PreEvent](event: T): T
或处理程序匹配语句进行任何更改,以帮助 Scala 意识到,当我匹配事件参数并复制该显式事件时,默认情况下它将与参数具有相同的类型,因此类型绑定?
import java.util.UUID
import scala.util.chaining._
trait Event
trait PreEvent
trait Handler {
def handle(event: Event): Unit = {}
def reduce[T <: PreEvent](event: T): T = event
}
class EventService {
private var handlers: List[Handler] = Nil
def publish(event: Event): Unit =
handlers.foreach { _.handle(event) }
def publish[T <: PreEvent](event: T): T =
handlers.foldLeft(event) { (event, handler) => handler.reduce(event) }
}
// this works fine
case class ConnectEvent(id: UUID) extends Event
class ConnectHandler extends Handler {
override def handle(event: Event): Unit = event match {
case ConnectEvent(id) =>
case _ =>
}
}
// problem 1
case class PreConnectEvent(id: UUID, cancelled: Boolean = false) extends PreEvent
class PreConnectHandler extends Handler {
override def reduce[T <: PreEvent](event: T): T = event match {
// (1) the copy result needs to be explicitly cast to an instance of T
case it: PreConnectEvent => it.copy(cancelled = true).asInstanceOf[T]
case _ => event
}
}
// problem 2
class Service(eventService: EventService) {
def publishPreEvent(): Unit = {
// (2) the method reference of 'eventService.publish' needs to be explicitly turned
// into an anonymous function with '(_)' or it tries to call EventService::publish(Event)
val reduced = PreConnectEvent(UUID.randomUUID()).pipe { eventService.publish(_) }
// do something with reduced event
}
// this works fine
def publishEvent(): Unit =
ConnectEvent(UUID.randomUUID()).tap { eventService.publish }
}
解决方案
我认为这是返回电流类型问题的一种变体。考虑类型类解决方案
trait Handler {
def handle(event: Event): Unit = {}
def reduce[T](event: T)(implicit ev: EventReducer[T]): T = ev.reduce(event)
}
trait EventReducer[T] {
def reduce(event: T): T
}
object EventReducer {
implicit val preConneectEventReducer: EventReducer[PreConnectEvent] =
(it: PreConnectEvent) => it.copy(cancelled = true)
implicit def otherEventReducer[T]: EventReducer[T] =
(event: T) => event
}
(new PreConnectHandler).reduce(PreConnectEvent(UUID.randomUUID()))
// res0: PreConnectEvent = PreConnectEvent(99bcd870-4b7d-4b28-a12a-eafe4da16c78,true)
(new PreConnectHandler).reduce(ConnectEvent(UUID.randomUUID()))
// res1: ConnectEvent = ConnectEvent(47af28b7-ea93-4da1-9ee6-e89d41540141)
推荐阅读
- postgresql - 当达到单页的列限制时,如何在下一页上打印时动态地在 ireport 上水平打印?
- dart - Dart / AngularDart - 如何创建图表/流程图?
- ansible - 过滤器:当参数是字符串 v/s 变量时的不同行为
- android - PathEffect 改变路径的长度
- python - 'pip install --upgrade pip' 的语法错误无效
- security - 为内部应用程序实施 SSO 身份验证的最佳实践,例如气流仪表板、apache spark 笔记本等
- c# - 在 WPF 画布中书写数千笔画的最佳方法
- html - ng-switch 不传递参数
- ios - 如何在没有 MFi 程序协议的情况下使用外部附件框架
- dynamics-crm - Dynamics CRM SLA 不会过期