隐式转换 - Scala:将 None 用于空 Option 以外的其他目的



根据文档,None对象旨在"表示不存在的值"。据我所知,它主要用作空Option。但是您认为将其用于其他目的是个好主意吗?例如,在我的库中,我希望有一个通用的"Empty"对象,它可以分配给各种缺失值,在那里我只是根据需要隐式地将"Empty"值转换为我的类型:

// In library:
trait A {
  implicit def noneToT1(none: Option[Nothing]): T1 = defaultT1
  implicit def noneToT2(none: Option[Nothing]): T2 = defaultT2
  def f1: T1
  def f2: T2
}
// In the code that uses the library
class EmptyA extends A {
  def f1 = None      
  def f2 = None      
}

不以这种方式(错误)使用None的一个原因是,用户希望f1f2分别返回Option[T1]Option[T2]。他们没有。当然,我可以def f1: Option[T1],但在这种情况下,这些值实际上并不是可选的,它们只是可以有一些默认的空值,或者一个真正的值,我只想在"引擎盖下"创建默认值,并在整个库中有一些统一的方式说"默认"或"空"。所以问题是,我应该使用 None 来表达这种"默认性"还是使用一些自定义类型?现在我正在使用自己的object Empty,但感觉有点多余。

编辑:为了说明我的问题,我将添加我现在使用的代码:

// In library:
trait Empty
object Empty extends Empty
trait A {
  implicit def emptyToT1(none: Empty): T1 = defaultT1
  implicit def emptyToT2(none: Empty): T2 = defaultT2
  def f1: T1
  def f2: T2
}
// In the code that uses the library
class EmptyA extends A {
  def f1 = Empty
  def f2 = Empty
}
class HalfFullA extends A {
  def f1 = Empty
  def f2 = someValue2
}
class FullA extends A {
  def f1 = someValue1
  def f2 = someValue2
}

我的问题很简单:使用 scala 的None而不是我的Empty是个好主意吗?

我只会为此使用类型类:

trait WithDefault[T] {
  def default: T
}
object WithDefault {
  // if T1 is an existing class
  implicit val t1Default = new WithDefault[T1] {def default = defaultT1}
}
//if T2 is your own class:
class T2 ...
object T2 {
  implicit val withDefault = new WithDefault[T2] {def default = defaultT2}
}

然后在方便的地方:

def default[T : WithDefault] = implicitly[WithDefault[T]].default

并使用:

class EmptyA {
  def f1 = default[T1]
  def f2 = default[T2]
}

更新:为了适应Vilius,可以尝试一下:

def default = new WithDefault[Nothing]{def default = error("no default")}
implicit def toDefault[U, T](dummy: WithDefault[U])(implicit withDefault: WithDefault[T]): T = withDefault.default
class EmptyA {
  def f1: T1 = default
  def f2: T2 = default
}

与OP的原始尝试相比,这具有优势,因为每个新类都可以定义自己的默认值(以及WithDefault中的其他类),而不是将所有内容都包含在特征A中。

但是,这不起作用。见 https://issues.scala-lang.org/browse/SI-2046

要解决此问题:

trait A {
    def f1: T1
    def f2: T2
    implicit def toT1Default(dummy: WithDefault[Nothing]) = toDefault[T1](dummy)
    implicit def toT2Default(dummy: WithDefault[Nothing]) = toDefault[T2](dummy)
}
class EmptyA extends A {
   def f1 = default
   def f2 = default
}

我认为你应该选择更简单的东西。例如,从您的示例开始并删除我们很快就会得到的无关内容,

trait A {
  def noT1 = defaultT1
  def noT2 = defaultT2
  def f1: T1
  def f2: T2
}
class EmptyA extends A {
  def f1 = noT1      
  def f2 = noT2      
}

我真的不认为添加选项或隐式内容会增加任何价值,至少除非这个问题还有其他未说明的上下文。

如果您不能或不想使用继承来定义默认值,我建议保留新对象。将None重用于Some对应物以外的其他内容似乎是错误的,并且并没有真正为您节省太多。

相关内容

最新更新