scala – 使用类型类的最佳方式,列表与一些基类,抽象类或特征参
我认为用具体的例子来描述一个问题会更容易.假设我有Fruit类层次结构和显示类型类:
trait Fruit case class Apple extends Fruit case class Orange extends Fruit trait Show[T] { def show(target: T): String } object Show { implicit object AppleShow extends Show[Apple] { def show(apple: Apple) = "Standard apple" } implicit object OrangeShow extends Show[Orange] { def show(orange: Orange) = "Standard orange" } } def getAsString[T](target: T)(implicit s: Show[T]) = s show target 我也有使用Show(这是我在这个问题的主要目标)给用户显示的水果列表: val basket = List[Fruit](Apple(),Orange()) def printList[T](list: List[T])(implicit s: Show[T]) = list foreach (f => println(s show f)) printList(basket) 这不会编译,因为List是由参数化的,而我没有定义任何Show [Fruit].使用类型类实现我的目标的最佳方式是什么? 我试图找到这个问题的解决方案,但不幸的是还没有找到任何好的.在printList函数中知道s还不够 – 不知何故需要知道列表中每个元素的Show [T].这意味着为了能够做到这一点,我们除了编译时还需要一些运行时机制.这给了我某种运行时字典的想法,知道如何在运行时找到通讯员[T]. 实现隐含的Show [Fruit]可以作为这样的字典: implicit object FruitShow extends Show[Fruit] { def show(f: Fruit) = f match { case a: Apple => getAsString(a) case o: Orange => getAsString(o) } } 实际上非常类似的方法可以在haskell中找到.例如,我们可以看一下Maye的Eq实现: instance (Eq m) => Eq (Maybe m) where Just x == Just y = x == y Nothing == Nothing = True _ == _ = False 这个解决方案的大问题是,如果我将添加如下的新的水果子类: case class Banana extends Fruit object Banana { implicit object BananaShow extends Show[Banana] { def show(banana: Banana) = "New banana" } } 并尝试打印我的篮子: val basket = List[Fruit](Apple(),Orange(),Banana()) printList(basket) 那么scala.MatchError将被抛出,因为我的字典不知道任何关于香蕉的东西.当然,我可以在一些了解香蕉的情况下提供更新的字典: implicit object NewFruitShow extends Show[Fruit] { def show(f: Fruit) = f match { case b: Banana => getAsString(b) case otherFruit => Show.FruitShow.show(otherFruit) } } 但这个解决方案远非完美.想象一下,其他的图书馆提供了另一个水果,它是自己的字典版本.如果我尝试一起使用它,它将与NewFruitShow冲突. 也许我错过了一些明显的东西 更新 在@Eric注意到,这里还有一个解决方案:forall in Scala.真的看起来很有趣但是我看到这个解决方案有一个问题. 如果我使用ShowBox,那么它将在创建时刻记住具体的类型类.所以我通常用对象和通信类型类建立列表(所以在列表中的字典).另一方面,scala具有非常好的功能:我可以在当前范围内删除新的含义,并覆盖默认值.所以我可以为类定义替代字符串表示形式,如: object CompactShow { implicit object AppleCompactShow extends Show[Apple] { def show(apple: Apple) = "SA" } implicit object OrangeCompactShow extends Show[Orange] { def show(orange: Orange) = "SO" } } 然后使用导入CompactShow._将其导入当前范围.在这种情况下,AppleCompactShow和OrangeCompactShow对象将被隐式使用,而不是在Show的伴随对象中定义的默认值.而您可以猜到,列表创建和打印会发生在不同的地方.如果我将使用ShowBox,大概我将捕获类型类的默认实例.我想在最后可能的时刻捕获它们 – 当我打印printList的那一刻,因为我甚至不知道,我的List [Fruit]是否会被显示或者如何被显示在创建它的代码中. 解决方法
最明显的答案是使用密封性状水果和水果[水果].这样你的模式匹配会在编译时抱怨,当比赛并不详尽.当然,在外部图书馆里添加一种新的水果是不可能的,但这是事物本质所固有的.这是“
expression problem”.
您还可以将“显示”实例粘贴在“水果”特征上: trait Fruit { self => def show: Show[self.type] } case class Apple() extends Fruit { self => def show: Show[self.type] = showA } 或者,您知道,停止子类型并使用类型类. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |