scala - Scala 类型类模式与模式匹配或重载
问题描述
我正在努力想出一个好的心智模型,什么时候问题适合类型类模式?
最近我正在使用像这样的模型
sealed trait FooBar
case class Foo() extends FooBar
case class Bar() extends FooBar
直觉上我会简单地进行模式匹配
def handle(x: FooBar) = x match {
case f: Foo => println("foo")
case b: Bar => println("bar")
}
或明确地使用子类型/重载
object Overloading {
def handle(x: Foo) = println("foo")
def handle(x: Bar) = println("bar")
}
另一方面,类型类方法很冗长,我看不出使用它有什么好处:
trait FooBarThing[T <: FooBar] {
def handle(x: T): Unit
}
object TypeClass {
implicit object HandleFoo extends FooBarThing[Foo] {
def handle(x: Foo) = println("foo")
}
implicit object HandleBar extends FooBarThing[Bar] {
def handle(x: Bar) = println("bar")
}
def process[T <: FooBar](x: T)(implicit ev: FooBarThing[T]): Unit = {
ev.handle(x)
}
}
我发现很多文章解释了如何编写类型类,但没有太多关于何时?
解决方案
Typeclass 模式提供了实现ad-hoc 多态性的可能性。也就是说,如果您有一些foobar
必须与许多不同类型一起使用的多态函数T
,然后您有一些T1
不实现任何提供的接口的具体类型foobar
,您可以按如下方式附加foobar
到T1
临时方式:
trait FoobarTypeclass[T] {
def foobar(t: T): Unit
}
def functionThatRequiresFoobar[T: FoobarTypeclass](t: T): Unit = {
for (i <- 1 to 10)
implicitly[FoobarTypeclass[T]].foobar(t)
}
// note that `functionThatRequiresFoobar` knows nothing about `T1` at this point
class T1
implicit object AdHocFoobarForT1 extends FoobarTypeclass[T1] {
def foobar(t: T1): Unit = println("foobar now works on T1, awesome!")
}
functionThatRequiresFoobar(new T1) // but here, it works anyway!
在上面的示例中,您可以看到两件事:
- 既不需要
FoobarTypeclass
也不functionThatRequiresFoobar
需要知道具体类型的存在T1
- 该类型也必须对or
T1
一无所知。FoobarTypeclass
functionThatRequiresFoobar
这意味着T1
和functionThatRequiresFoobar
是完全解耦的。但是在示例的最后一行中,
functionThatRequiresFoobar(new T1)
无论如何,工作得很好,因为类型类以特别的方式AdHocFoobarForT1
将实现附加foobar
到类。T1
类似地,您可以使用此模式在未在其继承层次结构中声明任何相关接口的类上“以特殊方式实现接口”。这反过来又允许您通过在这里和那里提供一些类型类来将完全独立的库粘合在一起。
推荐阅读
- sql-server - 检查sql server中特定月份的日期是否存在
- javascript - 用于调查结果的 Firebase 数据库
- asp.net - 在 asp.net mvc 中编辑已部署网站的控制器或模型而不接触数据库
- laravel-5 - Laravel 5.6/Bootstrap 4.1.0 应用程序中的不同颜色主题
- angular - 无法在 mapbox-gl 弹出窗口中加载角度组件
- sql - SQL Server:非聚集日期索引
- laravel - ErrorException (E_ERROR) 试图获取非对象的属性“生日”
- mysql - 选择作为孩子没有特定价值的记录
- spring - spring security标签库sec:授权url不起作用
- javascript - 反应原生谷歌登录失败。错误代码 10