Scala函数中的类型绑定使方法参考的管道复杂化 [英] Type bound in Scala function complicates piping to method reference

查看:65
本文介绍了Scala函数中的类型绑定使方法参考的管道复杂化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了一个简单的事件服务,该服务允许发布只读事件(Event)和可取消事件(PreEvent).所有处理程序都会减少可取消的事件,并将结果返回给调用方.

I've implemented a simple event service that allows for the publishing of read-only events (Event), and cancellable events (PreEvent). Cancellable events are reduced by all handlers and the result is returned to the caller.

Event交互按预期方式工作,但是T <: PreEventPreEvent类型界限给了我一些问题:

The Event interaction works as expected, but the PreEvent type bound of T <: PreEvent is giving me some issues:

  • (1)匹配PreEvent时,必须将其副本显式转换为T,以使编译器认识到它与方法参数的类型相同.
  • (2)当尝试将PreEvent传递给方法引用时,编译器突然忘记了它正在处理PreEvent,并尝试调用publishEvent变体.
  • (1) When matching a PreEvent its copy has to be explicitly cast to T for the compiler to realise it is the same type as the method parameter.
  • (2) When attempting to pipe a PreEvent to a method reference the compiler suddenly forgets it is dealing with a PreEvent and attempts to call the Event variant of publish.

除了重命名EventService::publish(PreEvent)方法以避免歧义之外,我还可以对Handler::reduce[T <: PreEvent](event: T): T的类型范围进行任何更改以帮助Scala在通过该方法时实现(event: T)始终为PreEvent作为方法参考? (因此没有可用的类型信息,尽管我希望编译器可以从上下文中解决此问题)

Aside from renaming the EventService::publish(PreEvent) method to avoid the disambiguation, are there any changes I could make to the type bound of Handler::reduce[T <: PreEvent](event: T): T to help Scala realise (event: T) will always be a PreEvent when the method is passed as a method reference? (and thus has no type information available, eventhough I would have expected the compiler to figure this out from the context)

我可以对Handler::reduce[T <: PreEvent](event: T): T的类型限制或处理程序匹配语句进行任何更改以帮助Scala意识到,当我在事件参数上进行匹配并复制该显式事件时,默认情况下它将具有相同的类型作为参数并因此绑定类型?

Are there any changes I could make to the type bound of Handler::reduce[T <: PreEvent](event: T): T or handler match statement to help Scala realise that when I'm matching on the event parameter and copying that explicit event it will by default be of the same type as the parameter and thus the type bound?

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 }
}

推荐答案

关于您的第一个问题,请在此处查看详细信息

Regarding your first question, see details here

模式匹配中使用的抽象类型的类型不匹配

问题是def reduce[T <: PreEvent](event: T): T只是不正确的签名

The thing is that def reduce[T <: PreEvent](event: T): T is just incorrect signature for

event match {
  case it: PreConnectEvent => it.copy(cancelled = true)
  case _                   => event
}

如果在Scala中可能使用这样的语法,则正确的名称为def reduce[T is a subclass of PreEvent](event: T): T(<:表示是子类型").

Correct one would be def reduce[T is a subclass of PreEvent](event: T): T if such syntax were possible in Scala (<: means "is a subtype of").

请考虑使用类型类方法(请参见 @MarioGalic 的答案)或类型标签方法(请参见上面的链接).

Please consider type class approach (see @MarioGalic's answer) or type tags approach (see above link).

关于第二个问题,您可以写

Regarding your second question, you can write

val reduced = PreConnectEvent(UUID.randomUUID()).pipe(eventService.publish[PreConnectEvent])

指定您正在使用泛型方法的重载版本.好吧,它几乎不比eventService.publish(_)短.

specifying that you're using the overloaded version of method that is generic. Well, hardly it's shorter than eventService.publish(_).

这篇关于Scala函数中的类型绑定使方法参考的管道复杂化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆