Почему у Варианта нет метода сгиба?

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

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

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

map(ifSome).getOrElse(ifNone)

Есть ли лучше, чем использовать map + getOrElse?

4 ответа

Решение

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

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

или же

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

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

Редактировать:

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

opt.cata(fun, els)

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

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 s. (Хотя вы также можете связать fun Конечно, с составом функций - это зависит от того, хотите ли вы сосредоточиться на составлении функций или преобразовании значений.)

Я лично нахожу такие методы, как 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
}

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

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

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

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

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

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

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

Как упомянул Дебилски, вы можете использовать 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 }

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

Другие вопросы по тегам