Scala类型类模式与模式匹配或重载
我正在努力为找到一个好的心智模型,什么时候一个问题适合类型类模式? 最近我和一个模特一起工作,比如Scala类型类模式与模式匹配或重载,scala,design-patterns,functional-programming,pattern-matching,typeclass,Scala,Design Patterns,Functional Programming,Pattern Matching,Typeclass,我正在努力为找到一个好的心智模型,什么时候一个问题适合类型类模式? 最近我和一个模特一起工作,比如 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("b
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)
}
}
trait-FooBarThing[TTypeclass模式提供了实现特殊多态性的可能性。
也就是说,如果您有一些多态函数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!
无论如何都可以很好地工作,因为AdHocFoobarForT1
typeclass以特殊方式将foobar
的实现附加到类T1
类似地,您可以使用此模式“以特殊方式实现接口”在继承层次结构中不声明任何相关接口的类上。这反过来允许您只需在此处或那里提供一些TypeClass,就可以将完全独立的库粘合在一起。Andrey Tyukin回答了何时可以使用TypeClass,所以我只想补充一下为什么更喜欢模式匹配而不是重载w当FooBar
是密封类型或不需要特殊多态性时
通常,重载会给类型系统带来很多麻烦,并使隐式的使用更加困难。讨论了重载的进一步缺点,但其中包括:
- 难以将方法提升到函数
- 将隐式视图应用于重载函数的参数时存在歧义
我只会在为未连接类型提供相同功能的情况下使用重载,以创建更好的程序员体验,例如
对象打印机{
def打印(a:Bool):字符串=???
def打印(a:Int):字符串=???
}
由于您可以在子类型上进行模式匹配,我当然会使用该选项。问题是,决定哪种工具适合于此项工作通常是一件逐案处理的事情。您所描述的问题在我看来太抽象了。我们到底在处理什么?是像JSON序列化这样的交叉问题吗?还是仅仅是另一个piec业务逻辑e?案例类不应该有空的参数列表。在案例类上进行模式匹配时,通常提取参数值,而不是按类型进行匹配。例如Case Foo(x)=>/…现在可以使用x。
而不是case f:Foo=>/…
。如果不需要参数,请将其设置为case对象(匹配为case Foo=>/…
)。请参阅以获得一些解释。FooBar
是一个密封特性,不会有任何未知类型t
functionThatRequiresFoobar(new T1)