Почему реализация Monoid for Option от scalaz дважды оценивает функцию f2?

Определение моноида опции scalaz выглядит следующим образом:

implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = new Monoid[Option[A]] {
  def append(f1: Option[A], f2: => Option[A]) = (f1, f2) match {
    case (Some(a1), Some(a2)) => Some(Semigroup[A].append(a1, a2))
    case (Some(a1), None)     => f1
    case (None, Some(a2))     => f2
    case (None, None)         => None
  }

  def zero: Option[A] = None
}

f2 - это параметр передачи по имени, что означает, что каждый вызов будет оценивать выражение. Почему он должен оцениваться снова, если он только что был оценен при сопоставлении с образцом? Возврат Some(a2) должен быть тем же результатом, а выражение f2 может быть очень дорогим.

Я что-то упускаю?

Исходный код Option.scala от Scalaz


person coltfred    schedule 02.05.2013    source источник
comment
Вероятно, это пережиток мышления Haskell, где эквивалентное определение стоит дешево?   -  person Mysterious Dan    schedule 02.05.2013
comment
Вы проверяли это? Поместите в него немного println и проверьте.   -  person Felix    schedule 02.05.2013
comment
Да, я проверил это, и println попал дважды. Я исправлю это и отправлю запрос на включение в scalaz, я думаю.   -  person coltfred    schedule 03.05.2013


Ответы (1)


Мне кажется, что это было написано, чтобы подчеркнуть симметричность проблемы и для ясности, а не для скорости. Вы не можете просто отбросить ленивость второго аргумента, поскольку Semigroup определяет его таким образом, а в других случаях ленивость второго аргумента может быть существенной. Чтобы сохранить визуальное представление о симметрии задачи, вы, вероятно, захотите просто добавить

val g2 = f2  // Force evaluation
(f1, g2) match { ...

или что-то в этом роде.

(Было бы неплохо, если бы аргументы по имени можно было назвать ленивыми, чтобы автоматически запоминать их.)

person Rex Kerr    schedule 02.05.2013
comment
Я отправил его как запрос на вытягивание, и они объединили его, но ваше решение тоже сработает. github.com/scalaz/scalaz/pull/352 - person coltfred; 03.05.2013