Scala:将None用于空选项以外的其他用途

Scala:将None用于空选项以外的其他用途,scala,option,implicit-conversion,Scala,Option,Implicit Conversion,根据文档,None对象旨在“表示不存在的值”。据我所知,它主要用作空的选项。但是你认为把它用于其他目的是个好主意吗。例如,在我的库中,我希望有一个通用的“Empty”对象,可以为各种缺失的值分配该对象,我只需根据需要将“Empty”值隐式转换为我的类型: // In library: trait A { implicit def noneToT1(none: Option[Nothing]): T1 = defaultT1 implicit def noneToT2(none: Opti

根据文档,
None
对象旨在“表示不存在的值”。据我所知,它主要用作空的
选项
。但是你认为把它用于其他目的是个好主意吗。例如,在我的库中,我希望有一个通用的“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      
}
不以这种方式(mis)使用
None
的一个原因是,用户希望
f1
f2
分别返回
选项[T1]
选项[T2]
。他们没有。当然,我可以有
def f1:Option[T1]
,但在这种情况下,这些值实际上不是可选的,它们可以有一些默认的空值,或者一个实值,我只想在“引擎盖下”创建默认值,并在整个库中以某种统一的方式表示“默认”或“空”。所以问题是,我应该使用
None
来表示这种“默认”还是使用某种自定义类型?现在我正在使用自己的
对象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
,这是一个好主意吗?

我会使用TypeClass来实现这一点:

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最初的尝试有好处,因为每个新类都可以定义自己的默认值(以及其他默认值),而不是将所有内容都放在trait
a

然而,这不起作用。看

要解决此问题,请执行以下操作:

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
对应项之外的其他内容似乎是错误的,并不会真正为您节省太多。

问题是,我想用一个词来表达
noT1
noT2
,可能还有许多其他
noXX
,这个词在我的库中是统一的。在某些情况下,这可能是一个空列表或集合,在另一种情况下,这可能是一个执行某些默认“空”操作的部分函数,或其他一些操作。我想把所有的
Nil
Set.empty
,或者
{case}
隐藏在一个神奇的单词表达式下面。问题是,我应该为此目的使用
None
,还是发明其他东西(我现在使用
objectempty
)?这是一个很好的解决方案,我还没有想到。我喜欢它,因为您有一种创建默认值的统一方法,但仍然不会让用户混淆方法的类型。然而,在这个特定的库中,这可能有点过头了,而且看起来仍然很难看——例如,我的一些方法返回部分函数,因此我必须编写:
default[PartialFunction[SomeType,OtherType]]
(当然我可能有类型别名,但这更像是过头了)Scala在这种情况下不能推断类型参数吗?更新了我的答案。Scala无法推断类型,因为if推断表达式类型并从中推断方法的返回类型,反之亦然。