加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 使用类型类的最佳方式,列表与一些基类,抽象类或特征参

发布时间:2020-12-16 09:10:11 所属栏目:安全 来源:网络整理
导读:我认为用具体的例子来描述一个问题会更容易.假设我有Fruit类层次结构和显示类型类: trait Fruitcase class Apple extends Fruitcase class Orange extends Fruittrait Show[T] { def show(target: T): String}object Show { implicit object AppleShow exte
我认为用具体的例子来描述一个问题会更容易.假设我有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
}

或者,您知道,停止子类型并使用类型类.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读