Scala: использование None для других целей, кроме пустого Option - PullRequest
4 голосов
/ 22 мая 2011

Согласно документам, объект 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 таким образом, заключается в том, что пользователь ожидает, что f1 и f2 вернут Option[T1] и Option[T2] соответственно. И они не делают. Конечно, я мог бы иметь def f1: Option[T1], но в этом случае значения на самом деле не являются необязательными, они просто могут иметь какое-то пустое значение по умолчанию или реальное значение, я просто хочу создать значения по умолчанию «под капотом» и иметь какой-то единый способ сказать «по умолчанию» или «пусто» через всю библиотеку. Таким образом, вопрос заключается в том, должен ли я использовать None для выражения этой "неплатежеспособности" или перейти на какой-то другой тип? Прямо сейчас я использую свой собственный object Empty, но он кажется немного излишним.

EDIT: Чтобы проиллюстрировать мой вопрос, я добавлю код, который я использую сейчас:

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

Мой вопрос довольно прост: стоит ли использовать None от scala вместо моего Empty?

Ответы [ 3 ]

7 голосов
/ 22 мая 2011

Для этого я бы просто использовал классы типов:

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

Обновление: для размещения Вилиуса, можно попробовать это:

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
}
3 голосов
/ 22 мая 2011

Я думаю, вы должны пойти на что-то гораздо более простое.Например, начиная с вашего примера и удаляя посторонние вещи, к которым мы очень быстро добираемся,

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

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

Я действительно не вижу, чтобы добавление опций или последствий для этого добавило бы какую-либо ценность, по крайней мере, еслиесть еще один неустановленный контекст для вопроса.

1 голос
/ 29 мая 2011

Если вы не можете или не хотите определять значение по умолчанию, используя наследование, я предлагаю сохранить новый объект. Повторное использование None для чего-то другого, чем Some, кажется неправильным и не особо вас спасает.

...