Scala函数中的类型绑定使方法参考的管道复杂化 [英] Type bound in Scala function complicates piping to method reference
问题描述
我已经实现了一个简单的事件服务,该服务允许发布只读事件(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 <: PreEvent
的PreEvent
类型界限给了我一些问题:
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
,并尝试调用publish
的Event
变体.
- (1) When matching a
PreEvent
its copy has to be explicitly cast toT
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 aPreEvent
and attempts to call theEvent
variant ofpublish
.
除了重命名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屋!