Может ли последовательность по infinte Maybes когда-либо прекратить?

Другими словами, можно ли оптимизировать следующее Just [1..]?

> sequence (map Just [1..])
*** Exception: stack overflow

Существует также более конкретный пример в data61/fp-course где досрочное прекращение ожидается, если Empty значение присутствует.

seqOptional ::
  List (Optional a)
  -> Optional (List a)
seqOptional =
    foldRight f (Full Nil)
      where
          f Empty _ = Empty
          f _ Empty = Empty
          f (Full a) (Full as) = Full (a :. as)

Почему изменение порядка первых двух шаблонов делает цикл функции вечным, как если бы Empty не может быть сопоставлен? Я смутно понимаю, что такое определение сделает f строг в бесконечном списке, но я не вижу, что на самом деле вызывает это.

Или это не связанные проблемы?

Дополнительный вопрос: имеет ли значение, что стек исчерпан, а не куча?

2 ответа

Даже если бы мог, не должен. Как и в комментарии @user2407038, согласно денотационной семантике Хаскелла, sequence (map Just [1..]) обозначает другое значение, чем Just [1..],

Функции Haskell являются непрерывными, что является ключевым инструментом для точных рассуждений о бесконечных структурах данных. Чтобы проиллюстрировать, что означает непрерывность, предположим, что у нас есть бесконечная последовательность значений, которые все чаще определяются, например:

⟂
1:⟂
1:2:⟂
1:2:3:⟂

Теперь примените функцию к каждому из них, скажем, tail:

tail ⟂             = ⟂
tail (1:⟂)         = ⟂
tail (1:2:⟂)       = 2:⟂
tail (1:2:3:⟂)     = 2:3:⟂
     ⋮                 ⋮
tail [1..]         = [2..]

Что означает непрерывность функции, так это то, что если вы применяете функцию к пределу последовательности аргументов, вы получаете предел последовательности результатов, как показано в последней строке.

Теперь некоторые замечания о sequence в частично определенных списках:

-- a ⟂ after a bunch of Justs makes the result ⟂
sequence (Just 1 : Just 2 : ⟂) = ⟂
-- a Nothing anywhere before the ⟂ ignores the ⟂ (early termination)
sequence (Just 1 : Nothing : ⟂) = Nothing

Нам нужно только первое наблюдение. Теперь мы можем задать ваш вопрос:

sequence (map Just ⟂)       = sequence ⟂                     = ⟂
sequence (map Just (1:⟂))   = sequence (Just 1 : ⟂)          = ⟂
sequence (map Just (1:2:⟂)) = sequence (Just 1 : Just 2 : ⟂) = ⟂
          ⋮                                   ⋮                 ⋮
sequence (map Just [1..])                                    = ⟂

Так что по преемственности, sequence (map Just [1..]) = ⟂, Если вы "оптимизировали" его, чтобы дать другой ответ, эта оптимизация была бы неправильной.

Я не могу ответить на ваш второй вопрос, но могу ответить на ваш первый.

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

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