Почему у Option нет метода сгиба? - PullRequest
35 голосов
/ 16 марта 2011

Интересно, почему scala.Option не имеет метода fold, как это определено:

fold(ifSome: A => B , ifNone: => B)

эквивалентно

map(ifSome).getOrElse(ifNone)

Нет ли лучше, чем map + getOrElse?

Ответы [ 4 ]

67 голосов
/ 17 марта 2011

Лично я нахожу методы типа cata, которые принимают два замыкания, поскольку аргументы часто переусердствуют. Вы действительно получаете читаемость более map + getOrElse? Подумайте о новичке в вашем коде: что они сделают из

opt cata { x => x + 1, 0 }

Вы действительно думаете, что это яснее, чем

opt map { x => x + 1 } getOrElse 0

На самом деле я бы сказал, что ни один из них не предпочтительнее старого доброго

opt match {
  case Some(x) => x + 1
  case None => 0
}

Как всегда, существует предел, в котором дополнительная абстракция не дает вам преимуществ, а превращает ее в контрпродуктивную.

41 голосов
/ 02 февраля 2013

Наконец-то добавлено в Scala 2.10 , с подписью fold[B](ifEmpty: => B)(f: A => B): B.

К сожалению, это имеет общее негативное последствие: B выводится для вызовов только на основе аргумента ifEmpty, который на практике часто является более узким. Например. (правильная версия уже есть в стандартной библиотеке, это только для демонстрации)

 def toList[A](x: Option[A]) = x.fold(Nil)(_ :: Nil)

Scala выведет B на Nil.type вместо желаемого List[A] и будет жаловаться на то, что f не вернет Nil.type. Вместо этого вам нужен один из

 x.fold[List[A]](Nil)(_ :: Nil)
 x.fold(Nil: List[A])(_ :: Nil)

Это делает fold не совсем эквивалентным соответствующему match.

25 голосов
/ 16 марта 2011

Вы можете сделать:

opt foldLeft (els) ((x, y) => fun(x))

или

(els /: opt) ((x,y) => fun(x))

(Оба решения будут оценивать els по значению, которое может быть не тем, что вы хотите. Спасибо Рекс Керр за указание на него.)

Edit:

Но что вам действительно нужно, так это катаморфизм Скалаза cata (в основном fold, который не только обрабатывает значение Some, но и отображает часть None, что вы и описали)

opt.cata(fun, els)

определяется как (где value - это значение параметра pimped)

def cata[X](some: A => X, none: => X): X = value match {
  case None => none
  case Some(a) => some(a)
}

, что эквивалентно opt.map(some).getOrElse(none).

Хотя я должен отметить, что вы должны использовать cata только тогда, когда это «более естественный» способ выразить это. Есть много случаев, когда достаточно простого map - getOrElse, особенно когда оно включает в себя потенциально много map с. (Хотя вы, конечно, можете также связать fun с композицией функций - это зависит от того, хотите ли вы сосредоточиться на композиции функций или преобразовании значений.)

19 голосов
/ 17 марта 2011

Как упомянул Дебилски, вы можете использовать Scalaz's OptionW.cata или fold. Как прокомментировал Джейсон, именованные параметры выглядят хорошо:

opt.fold { ifSome = _ + 1, ifNone = 0 }

Теперь, если в случае None вы хотите значение mzero для некоторых Monoid[M] и у вас есть функция f: A => M для случая Some, вы можете сделать это:

opt foldMap f

Итак,

opt map (_ + 1) getOrElse 0

становится

opt foldMap (_ + 1)

Лично я думаю, что Option должен иметь apply метод, который был бы катаморфизмом. Таким образом, вы можете просто сделать это:

opt { _ + 1, 0 }

или

opt { some = _ + 1, none = 0 }

На самом деле это было бы неплохо для всех алгебраических структур данных.

...