Scala:将 None 用于除空选项之外的其他目的

发布于 2024-11-09 14:49:01 字数 1426 浏览 0 评论 0原文

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

// 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]选项[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 是一个好主意吗?

According to the docs, None object is intended to "represent non-existent values". As far as I've seen it's mostly used as an empty Option. But do you think it's a good idea to use it for other purposes. For example, in my library I want to have an universal "Empty" object which could be assigned for various missing values, where I would just implicitly convert the "Empty" value to my types as needed:

// 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      
}

One reason for not (mis)using None in this fashion is that the user would expect that f1 and f2 return Option[T1] and Option[T2] respectively. And they don't. Off course, I could have def f1: Option[T1], but in this case the values are not actually optional, they just can have some default empty value, or a real value, I just want to create the default values "under the hood" and have some uniform way of saying "default" or "empty" through the entire library. So the question is, should I use None to express this "defaultness" or go for some custom type? Right now I'm using my own object Empty, but it feels a bit superfluous.

EDIT:
To ilustrate my question I'll add the code I am using right now:

// 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
}

My question is quite simple: is it a good idea to use scala's None instead of my Empty?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

话少心凉 2024-11-16 14:49:01

我只想为此使用类型类:

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
}

I would just use typeclasses for this:

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}
}

then somewhere convenient:

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

and use:

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

Update: To accomudate Vilius, one can try this:

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
}

This has the benefit over the OP's original attempt in that each new class can define its own default (and others in WithDefault), rather than have everything in a trait A.

However, this doesn't work. See https://issues.scala-lang.org/browse/SI-2046

To work around this:

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
}
2024-11-16 14:49:01

我认为你应该做一些更简单的事情。例如,从您的示例开始并删除我们很快就会遇到的无关内容,

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

class EmptyA extends A {
  def f1 = noT1      
  def f2 = noT2      
}

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

I think you should go for something much simpler. For instance, starting with your example and deleting extraneous stuff we very quickly get to,

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

class EmptyA extends A {
  def f1 = noT1      
  def f2 = noT2      
}

I really don't see that the addition of Options or implicits to this would add any value, at least not unless there's some other unstated context for the question.

表情可笑 2024-11-16 14:49:01

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

If you can't or don't want to define the default value using inheritance, I suggest to keep the new object. Reusing None for something else than the Some counterpart seems wrong and doesn't really save you much.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文