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)