【问题标题】:Drawbacks of using typeclasses in scala在 scala 中使用类型类的缺点
【发布时间】:2014-03-21 18:36:20
【问题描述】:

有些框架完全包含类型类模式。 scalaz 和 shapeless 就是很好的例子。因此,在某些情况下,类型类肯定比普通的 java 类和多态更可取。

我敬畏隐含的证据表达能力,我很好奇为什么这种方法缺乏实际应用。是什么原因迫使 Scala 程序员使用基本类。类型类显然会花费冗长和运行时间,但还有其他原因吗?

我没有以前的 java 经验就来到了 scala,我想知道我是否错过了经典 scala-java 类可能带来的一些基本好处。

我正在寻找一些引人注目的用例来展示类型类不足或无效的领域。

【问题讨论】:

  • 我不确定我买这个问题的前提(至少是“缺乏实际应用”部分)。类型类在 Scala 标准库中发挥着核心作用(如果你眯着眼睛看Comparator,它甚至会出现在 Java 中)。
  • scalaz 和 shapeless 用 monad 抽象重新实现了其中的许多。选择 scala 标准库肯定有一些原因,但其中的经典类并不是杀手级功能,因为类型类满足相同的用例
  • 我撤回我的回答。这个问题会产生意见。
  • 冗长毕竟是一个很好的解释。我只是认为可能有更强有力的理由

标签: scala typeclass static-typing


【解决方案1】:

类型类和继承支持以不同方式重用。继承擅长为更改的内部结构提供正确的功能。

class Foo { def foo: String = "foo" }
def fooUser(foo: Foo) { println(foo.foo) }

class Bar extends Foo {
  private var annotation = List.empty[String]
  def annotate(s: String) { annotation = s :: annotation }
  override def foo = ("bar" :: annotation.map("@" + _)).mkString(" ")
}

现在,使用Foo 的每个人都可以得到正确的值,只要你给他们Bar,即使他们只知道类型是Foo。您不必预料到您可能需要可插入功能(除非不标记 foo final)。您不需要跟踪类型或继续向前传递见证实例;您只需使用Bar 代替Foo,它会做正确的事情。这是一个大问题。如果您想要一个具有易于修改的功能的固定接口,那么继承就是您的选择。

相比之下,当您拥有一组固定的数据类型和易于修改的接口时,继承就不那么好了。排序就是一个很好的例子。假设您要对Foo 进行排序。如果你尝试

class Foo extends Sortable[Foo] {
  def lt(you: Foo) = foo < you.foo
  def foo = "foo"
}

你可以将它传递给任何可以对Sortable 进行排序的东西。但是,如果您想按名称长度而不是标准排序怎么办?嗯,

class Foo extends LexicallySortable[Foo] with LengthSortable[Foo] {
  def lexicalLt(you: Foo) = foo < you.foo
  def lengthLt(you: Foo) = foo.length < you.foo.length
  def foo = "foo"
}

这很快就会变得毫无希望,尤其是因为您必须寻找 Foo 的所有子类并确保它们正确更新。最好将小于计算推迟到可以根据需要换出的类型类。 (或者你必须始终明确引用的常规类。)这种自动选择的功能也很重要。

你不能真正用另一个替换一个。当您需要轻松地将新类型的数据合并到固定接口时,请使用继承。当您需要几种基础数据但需要轻松提供新功能时,请使用类型类。当您需要两者时,无论您采取哪种方式,您都会有很多工作要做,所以请习惯于品尝。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
  • 2020-08-04
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多