【问题标题】:Type Class Pattern alternative for enriching ADT with methods in Scala使用 Scala 中的方法丰富 ADT 的类型类模式替代方案
【发布时间】:2017-06-29 18:11:46
【问题描述】:

我写了一个基本的代数数据类型定义如下

sealed trait Fruit {def name: String}
case class Apple(name: String) extends Fruit
case class Orange(name: String, color: String) extends Fruit

我想做的是定义一个跨 Apple 和 Orange 的通用方法。所以我决定通过 Type Class 模式来提供这个特性。

sealed trait ServingFruit[T] {
    def peel(fruit: T): String
}

object FruitManager {
    def retrievePeelColor[T: ServingFruit](fruit: T): String =
        implicitly[ServingFruit[T]].peel(fruit)

    implicit object ApplePeelColor extends ServingFruit[Apple] {
        def peel(fruit: Apple): String = "GREEN"
    }

    implicit object OrangePeelColor extends ServingFruit[Orange] {
        def peel(fruit: Orange): String = fruit.color
    }
}

对于一个必要的(和不幸的)约束,我必须将水果作为 共享基本特征的有界实例 Fruit

def myCodeMethod[F <: Fruit](fruit: F, out: String) = {
    import FruitManager._
    FruitManager.retrievePeelColor(fruit)
}

这让我想到了以下(以某种方式预期的)异常。

could not find implicit value for evidence parameter of type my.path.to.fruit.ServingFruit[F] [error]FruitManager.retrievePeelColor(fruit)

然后,AFAIU herehere 类型类模式是类型独立的,也许后者不太适合我的场景。

关键是我正在努力寻找一个有价值的解决方案,将我的 ADT 与基本特征也可用的通用方法集成在一起,同时我想避免在 ADT 中提供这些方法(我会尝试保持面向 FP)并使用这样的变通方法,例如在我的类型类中添加一个额外的Fruit 转换器。

非常感谢任何帮助,谢谢。

安德烈亚

【问题讨论】:

    标签: scala functional-programming typeclass algebraic-data-types


    【解决方案1】:

    您需要将类型类见证作为隐式参数传递给retrievePeelColor

    scala> def retrievePeelColor[T](fruity: T)(implicit peeler: ServingFruit[T]) = peeler.peel(fruity)
    retrievePeelColor: [T](fruity: T)(implicit peeler: ServingFruit[T])String
    
    scala> retrievePeelColor(Apple("granny smith"))
    res0: String = GREEN
    
    scala> retrievePeelColor(Orange("bloody", "RED"))
    res1: String = RED
    

    至于设计:我不是一个经验丰富的设计人员,但我不会说在sealed trait 中有一些方法不是“FP 风格”。如果只有水果是可剥的,并且数量有限,那么可以为peel 留一个位置,恕我直言(我说的是Fruit 中的一种方法@ 匹配this,或者在Fruit 伴随对象)。

    【讨论】:

    • 这仍然不能解决问题:如果我从myCodeMethod 调用该方法,它仍然会寻找隐含的证据ServingFruit[F],其中F &lt;: Fruit。当然,我也不是设计专家,但我想避免通过在Fruit 中声明它并为每个水果定义它来提供该方法,因为我想保持代码语义与水果 ADT 分离的逻辑。
    • FruitManager 重命名为 ServingFruit 即可。或者只是import FruitManager._
    猜你喜欢
    • 2019-03-04
    • 1970-01-01
    • 2011-10-12
    • 2017-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    相关资源
    最近更新 更多