据我所知,Scala中的this blog post "type classes"只是一个用特征和隐式适配器实现的"pattern" .
正如博客所说,如果我有特性 A
和适配器 B -> A
那么我可以调用一个函数,它需要类型 A
的参数,参数类型为 B
,而不显式调用此适配器 .
我发现它很好但不是特别有用 . 您能给出一个用例/示例,它显示了此功能的用途吗?
据我所知,Scala中的this blog post "type classes"只是一个用特征和隐式适配器实现的"pattern" .
正如博客所说,如果我有特性 A
和适配器 B -> A
那么我可以调用一个函数,它需要类型 A
的参数,参数类型为 B
,而不显式调用此适配器 .
我发现它很好但不是特别有用 . 您能给出一个用例/示例,它显示了此功能的用途吗?
11 回答
一个用例,根据要求......
想象一下,你有一个列表,可以是整数,浮点数,矩阵,字符串,波形等 . 鉴于此列表,您想要添加内容 .
一种方法是使一些
Addable
特性必须由可以一起添加的每种类型继承,或者如果处理来自第三方库的对象无法将接口改为接口,则隐式转换为Addable
.当您还想开始添加可以对对象列表执行的其他此类操作时,这种方法会变得很快 . 如果你需要替代方案,它也不能很好地工作(例如;添加两个波形会连接它们,还是覆盖它们?)解决方案是ad-hoc多态,您可以在其中挑选和选择要改装到现有类型的行为 .
对于原始问题,您可以实现
Addable
类型类:然后,您可以创建此隐式子类实例,对应于您希望添加的每种类型:
总结列表然后写入的方法变得微不足道......
这种方法的优点在于,您可以提供某种类型类的替代定义,或者通过导入控制范围内的隐式,或者通过显式提供其他隐式参数 . 因此,可以提供添加波形的不同方法,或指定整数加法的模运算 . 从某些第三方库向类型类添加类型也相当轻松 .
顺便说一下,这正是2.8集合API采用的方法 . 虽然
sum
方法是在TraversableLike
而不是List
上定义的,但类型类是Numeric
(它还包含一些操作,而不仅仅是zero
和append
)重读那里的第一条评论:
我认为这是类型类最重要的优势 .
此外,它们正确处理操作没有我们正在调度的类型的参数或具有多个参数的情况 . 例如 . 考虑这种类型:
我认为类型类是向类中添加类型安全元数据的能力 .
因此,您首先定义一个类来为问题域建模,然后考虑要添加到其中的元数据 . 像Equals,Hashable,Viewable等等 . 这会创建问题域和机制的分离,以使用类并打开子类,因为类更精简 .
除此之外,您可以在范围内的任何位置添加类型类,而不仅仅是定义类的位置,还可以更改实现 . 例如,如果我使用Point#hashCode计算Point类的哈希码,那么我仅限于那个特定的实现,它可能无法为我具有的特定Point集创建良好的值分布 . 但是如果我使用Hashable [Point],那么我可以提供自己的实现 .
[更新示例]作为示例,这是我上周使用的一个用例 . 在我们的产品中,有几种包含容器作为值的 Map . 例如,
Map[Int, List[String]]
或Map[String, Set[Int]]
. 添加到这些集合可能很冗长:所以我想要一个包装它的功能,这样我就可以写了
主要问题是集合并不都具有添加元素的相同方法 . 有些人有'而有些人':' . 我还想保留向列表添加元素的效率,所以我不想使用创建新集合的fold / map .
解决方案是使用类型类:
在这里,我定义了一个类型类
Addable
,可以将元素C添加到集合CC . 我有2个默认实现:对于使用::
的列表和其他集合,使用构建器框架 .然后用这个类型是:
特殊位使用
adder.add
添加元素,使用adder.empty
为新键创建新集合 .为了比较,没有类型类,我将有3个选项:1 . 为每个集合类型编写一个方法 . 例如,
addElementToSubList
和addElementToSet
等 . 这在实现中创建了许多样板并污染了名称空间2.使用反射来确定子集合是否是List / Set . 这是很棘手的,因为 Map 是空的开始(当然scala也帮助这里也有Manifests)3 . 通过要求用户提供加法器来让穷人的类型类 . 像addToMap(map, key, value, adder)
这样的东西很简单我发现这篇博文有用的另一种方式是描述类型类的地方:Monads Are Not Metaphors
在文章中搜索类型类 . 这应该是第一场比赛 . 在本文中,作者提供了Monad类型类的示例 .
查看类型类的一种方法是它们启用 retroactive extension 或 retroactive polymorphism . Casual Miracles和Daniel Westheide有一些很棒的帖子显示了在Scala中使用Type Classes来实现这一目的的示例 .
这是一个post on my blog,它探索了 retroactive supertyping 的scala中的各种方法,这是一种追溯扩展,包括类型类示例 .
论坛帖子“What makes type classes better than traits?”提出了一些有趣的观点:
我不知道除了 Ad-hoc polymorhism 以外的任何其他用例,这是最好的解释方法 .
implicits和类型类都用于 Type-conversion . 两者的主要用例是提供 ad-hoc polymorphism (即)类,你可以_992191的排序函数) . 有关详细信息,请参阅https://lakshmirajagopalan.github.io/diving-into-scala-typeclasses/
在scala类型中
启用ad-hoc多态
静态类型(即类型安全)
借用哈斯克尔
解决表达式问题
行为可以在事后 - 在编译时 - 扩展 - 无需更改/重新编译现有代码
Scala Implicits
方法的最后一个参数列表可以隐式标记
隐式参数由编译器填写
实际上,您需要编译器的证据
...例如范围内存在类型类
如果需要,您还可以显式指定参数
下面带有类型类实现的String类的示例扩展下面使用新方法扩展了类,即使string是final :)
This是一个重要的区别(函数式编程需要):
考虑
inc:Num a=> a -> a
:收到的
a
与返回的相同,这不能用子类型完成我喜欢使用类型类作为依赖注入的轻量级Scala惯用形式,它仍然可以使用循环依赖,但不会增加很多代码复杂性 . 我最近改写了Scala project from using the Cake Pattern to type classes for DI并将代码大小减少了59% .