首页 > 解决方案 > 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)
  }
}

我发现很多文章解释了如何编写类型类,但没有太多关于何时

标签: scaladesign-patternsfunctional-programmingpattern-matchingtypeclass

解决方案


Typeclass 模式提供了实现ad-hoc 多态性的可能性。也就是说,如果您有一些foobar必须与许多不同类型一起使用的多态函数T,然后您有一些T1不实现任何提供的接口的具体类型foobar,您可以按如下方式附加foobarT1临时方式:

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!

在上面的示例中,您可以看到两件事:

  1. 既不需要FoobarTypeclass也不functionThatRequiresFoobar需要知道具体类型的存在T1
  2. 该类型也必须对orT1一无所知。FoobarTypeclassfunctionThatRequiresFoobar

这意味着T1functionThatRequiresFoobar完全解耦的。但是在示例的最后一行中,

functionThatRequiresFoobar(new T1)

无论如何,工作得很好,因为类型类以特别的方式AdHocFoobarForT1将实现附加foobar到类。T1

类似地,您可以使用此模式在未在其继承层次结构中声明任何相关接口的类上“以特殊方式实现接口”。这反过来又允许您通过在这里和那里提供一些类型类来将完全独立的库粘合在一起。


推荐阅读