gpt4 book ai didi

scala - 概括 Scala 类型类的问题

转载 作者:行者123 更新时间:2023-12-04 15:12:18 27 4
gpt4 key购买 nike

在处理使用类型类模式的 Scala 项目时,我遇到了语言如何实现模式的严重问题:由于 Scala 类型类实现必须由程序员而不是语言管理,因此任何变量属于一个类型类永远不会被注解为父类型,除非它的类型类实现被采用。

为了说明这一点,我编写了一个快速示例程序。想象一下,您正在尝试编写一个程序,该程序可以为公司处理不同类型的员工,并可以打印关于他们的进度的报告。要使用 Scala 中的类型类模式解决此问题,您可以尝试以下操作:

abstract class Employee
class Packer(boxesPacked: Int, cratesPacked: Int) extends Employee
class Shipper(trucksShipped: Int) extends Employee

一个对不同类型员工进行建模的类层次结构,足够简单。现在我们实现 ReportMaker 类型类。
trait ReportMaker[T] {
def printReport(t: T): Unit
}

implicit object PackerReportMaker extends ReportMaker[Packer] {
def printReport(p: Packer) { println(p.boxesPacked + p.cratesPacked) }
}

implicit object ShipperReportMaker extends ReportMaker[Shipper] {
def printReport(s: Shipper) { println(s.trucksShipped) }
}

这一切都很好,我们现在可以编写一些可能如下所示的 Roster 类:
class Roster {
private var employees: List[Employee] = List()

def reportAndAdd[T <: Employee](e: T)(implicit rm: ReportMaker[T]) {
rm.printReport(e)
employees = employees :+ e
}
}

所以这是有效的。现在,多亏了我们的类型类,我们可以将包装器或托运人对象传递给 reportAndAdd 方法,它会打印报告并将员工添加到名册中。但是,如果不显式存储传递给 reportAndAdd 的 rm 对象,就不可能编写一种方法来尝试打印出名册中每个员工的报告!

支持该模式的另外两种语言 Haskell 和 Clojure 不存在这个问题,因为它们处理了这个问题。 Haskell 全局地存储从数据类型到实现的映射,所以它总是“带有”变量,而 Clojure 基本上做同样的事情。这是一个在 Clojure 中完美运行的快速示例。
    (defprotocol Reporter
(report [this] "Produce a string report of the object."))

(defrecord Packer [boxes-packed crates-packed]
Reporter
(report [this] (str (+ (:boxes-packed this) (:crates-packed this)))))
(defrecord Shipper [trucks-shipped]
Reporter
(report [this] (str (:trucks-shipped this))))

(defn report-roster [roster]
(dorun (map #(println (report %)) roster)))

(def steve (Packer. 10 5))
(def billy (Shipper. 5))

(def roster [steve billy])

(report-roster roster)

除了将员工列表转换为 List[(Employee, ReportMaker[Employee]) 类型的相当讨厌的解决方案之外,Scala 是否提供了任何方法来解决这个问题?如果没有,既然 Scala 库广泛使用了类型类,为什么没有解决呢?

最佳答案

通常在 Scala 中实现代数数据类型的方式是使用 case类:

sealed trait Employee
case class Packer(boxesPacked: Int, cratesPacked: Int) extends Employee
case class Shipper(trucksShipped: Int) extends Employee

这为 Packer 提供了模式提取器和 Shipper构造函数,所以你可以匹配它们。

不幸的是, PackerShipper也是不同的(子)类型,但是在 Scala 中编码代数数据类型的模式的一部分是要遵守纪律以忽略这一点。相反,在区分包装商或托运人时,请像在 Haskell 中一样使用模式匹配:
implicit object EmployeeReportMaker extends ReportMaker[Employee] {
def printReport(e: Employee) = e match {
case Packer(boxes, crates) => // ...
case Shipper(trucks) => // ...
}
}

如果您没有其他类型需要 ReportMaker实例,那么也许不需要类型类,您可以使用 printReport功能。

关于scala - 概括 Scala 类型类的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18263978/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com