在Scala中定义ADT的首选方法是什么 - OO与FP

时间:2017-05-10 11:35:59

标签: scala functional-programming

经过一些讨论后,我想强调,这个问题与在产品/总和类型的OOP中使用继承有关,如在FP中,反之亦然。你喜欢什么,你的建议是什么!所以......

我有些怀疑 - 定义以下内容的更清晰(更清晰)的方法是什么:

例如我更喜欢

sealed trait Shape

case class Square(length: Int) extends Shape
case class Rhombus(length: Int) extends Shape

而不是

sealed abstract class Shape(val length: Int)

case class Square(override val length: Int) extends Shape(length)
case class Rhombus(override val length: Int) extends Shape(length)

但是

def draw(shape: Shape): String = shape match {
  case Square(length) => length.toString
  case Rhombus(length) => length.toString
}

而不是

def draw(shape: Shape): String = shape.length.toString

类型的定义在第一种方法中看起来更简洁,更一致,但如果我需要为所有形状设置通用逻辑,如draw方法,则第二种方法看起来更清晰。

3 个答案:

答案 0 :(得分:6)

这将是首选方式:

sealed trait Shape {
  def length: Int
}

case class Square(length: Int) extends Shape
case class Rhombus(length: Int) extends Shape

以下是您的代码中的一些非惯用内容:

  • abstract class主要是java兼容性的东西,尽可能使用特征
  • override未实现的字段感觉不对
  • 应该避免接口中的
  • val,总是使用defs作为摘要,这些可以用其他东西进行改进

答案 1 :(得分:0)

摘自本书https://leanpub.com/fpmortals/

  

我们也可以使用sealed trait代替sealed abstract class,但是使用abstract class具有二进制兼容性优势。仅当您需要创建具有多重继承的复杂ADT时,才需要sealed trait

答案 2 :(得分:0)

  

这不是一个坏建议,但对我来说,它是对Product类型的更多继承。有了特质,它可能更多是mixin,但是仍然!长度就是所有形状的一部分,即使您不需要它。

如果不是所有形状都需要它,那么就不用

def draw(shape: Shape): String = shape.length.toString

首先,没有得到它不是一个缺点。但是你也可以拥有

sealed trait Shape
sealed trait HasLength extends Shape {
  def length: Int
}

// Shape optional, but may be clearer for this case
case class Square(length: Int) extends Shape with HasLength
case class Rhombus(length: Int) extends Shape with HasLength

def draw(shape: Shape): String = shape match {
  case shape: HasLength => shape.length.toString
  case ... => ... // handle those shapes which don't have length
}