Может ли / должно быть неявное преобразование из T в Option [T] быть добавлено / создано в Scala? - PullRequest
35 голосов
/ 17 ноября 2009

Является ли это возможностью сделать вещи немного более эффективными (для программиста): я считаю, что становится немного утомительно, когда нужно упаковывать вещи в Some, например Some(5). Что-то вроде этого:

implicit def T2OptionT( x : T) : Option[T] = if ( x == null ) None else Some(x)

Ответы [ 6 ]

31 голосов
/ 17 ноября 2009

Вы потеряете некоторую безопасность типов и, возможно, вызовете путаницу. Например:

  val iThinkThisIsAList = 2 
  for (i <- iThinkThisIsAList) yield { i + 1 }

Я (по какой-то причине) подумал, что у меня есть список, и компилятор не поймал его, когда я перебрал его, потому что он был автоматически преобразован в Option [Int].

Я должен добавить, что я считаю, что импортировать явно неявно, просто, вероятно, не глобальное значение по умолчанию.

26 голосов
/ 17 ноября 2009

Обратите внимание, что вы можете использовать явный неявный шаблон *1001*, который позволит избежать путаницы и в то же время сохранить сжатый код.

Под явным неявным подразумевается не прямое преобразование из T в Option[T], а преобразование в объект-обертку, который предоставляет средства для преобразования из T в Option[T].

class Optionable[T <: AnyRef](value: T) {
  def toOption: Option[T] = if ( value == null ) None else Some(value)
}

implicit def anyRefToOptionable[T <: AnyRef](value: T) = new Optionable(value)

... Я мог бы найти для него лучшее имя, чем Optionable, но теперь вы можете написать код вроде:

val x: String = "foo"
x.toOption // Some("foo")

val y: String = null
x.toOption // None

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

Обратите внимание на T <: AnyRef - это неявное преобразование следует выполнять только для типов, которые допускают значения null, которые по определению являются ссылочными типами.

12 голосов
/ 17 ноября 2009

Общие рекомендации для неявных преобразований следующие:

  • Когда вам нужно добавить членов к типу (например, «открытые классы» или «шаблон pimp my library»), преобразуйте в новый тип, который расширяет AnyRef и который определяет только члены вам нужны.
  • Когда вам нужно «исправить» иерархию наследования. Таким образом, у вас есть некоторый тип A, который должен иметь подкласс B, но по какой-то причине этого не было. В этом случае вы можете определить неявное преобразование из A в B.

Это только случаи, когда уместно определить неявное преобразование. Любое другое преобразование в спешке сталкивается с проблемами безопасности и правильности типов.

На самом деле T не имеет никакого смысла расширять Option[T], и, очевидно, целью преобразования является не просто добавление членов. Таким образом, такое преобразование было бы нежелательным.

1 голос
/ 17 ноября 2009

Может показаться, что это может сбить с толку других разработчиков, поскольку они читают ваш код.

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

Возможно, вы захотите добавить код, показывающий, как он используется, чтобы увидеть, насколько запутанным он будет.

0 голосов
/ 07 марта 2014

Вы также можете попробовать перегрузить метод:

def having(key:String) = having(key, None)

def having(key:String, default:String) = having(key, Some(default))

def having(key: String, default: Option[String]=Option.empty) : Create = {
  keys += ( (key, default) )
  this
}
0 голосов
/ 17 ноября 2009

Это выглядит хорошо для меня, за исключением того, что оно может не работать для примитивного T (который не может быть нулевым). Я полагаю, что неспециализированный универсальный объект всегда получает коробочные примитивы, так что, вероятно, все в порядке.

...