2013-08-12 4 views
2

Не мог бы рассказать мне, почему этот фрагмент кода, который использует государственную монаду, никогда не заканчивается?Государство Монад никогда не заканчивается

fib' :: State [Int]() 
fib' = do l <- get 
      let l2 = sum (last2 l) 
      put (l ++ [l2]) 
      return() 
      fib' 

take 10 $ execState fib' [0, 1] 

Когда я выполняю его в REPL, ghci, функция выполняется без остановки.

ответ

1

Что вы делаете здесь более или менее эквивалентно следующему:

rfib (last:last':rest) = let new = last + last' in rfib (new:last:last':rest) 

который пытается создать числа в обратном порядке «лениво». (Приказ действительно не имеет значения, я просто сделал это в обратном порядке, чтобы избавиться от отвлекающих (++) и last2).

Но дело в том, что вы никогда не вычислите список, как скажет вам специалист по проверке типа. Например, происходит некорректно все хорошо напечатал:

res1 = (take 10 . rfib) [1,0] :: [Int] 
res2 = (take 10 . snd . rfib) [1,0] :: [Int] 
res3 = (take 10 . snd . snd . rfib) [1,0] :: [Int] 
res4 = "foo" == rfib [1,0] 

Иногда это хорошая идея не писать аннотации, вы знаете. Тогда вы увидите, был сделан вывод, что-то вроде этого:

rfib :: Num a => [a] -> b 

, которая соответствует близко к основному типу вашей Foo ':

fib' :: Num a => State [a] b 

И этот тип вы должны сделать немного подумать. В случае с rfib он сообщает, что из ниоткуда вы создаете значение любого типа, который вы хотите, здесь называется b. И это синоним «нет такого значения». Как и в случае head [] или unJust Nothing или error "Bottom", любая попытка все же вычислить это значение должна расходиться.

Вопросы различаются, когда переменная нового типа, которая появляется в результате, «защищена» конструктором типа. Тогда то, что произойдет, будет зависеть от типа, конструктор которого применяется. В его нынешнем виде он работает с Writer, но не с государством. Тем не менее, такой неожиданный тип должен заставлять думать об этом.

+1

Не совсем верно. Если вы возьмете его рекурсивный, бесконечный код, но поместите его в монаду-писателю, чтобы получить что-то типа 'fib [0,1] :: MonadWriter [Int] m => mb', тогда' возьмите 10 $ execWriter (fib [0, 1]) 'работает отлично, несмотря на' b' в возвращаемом типе. Таким образом, его общая попытка была не так разбита, как вы предполагаете, это была просто неправильная монада. –

+0

Это совсем другое, @JoachimBreitner, потому что тогда тип 'm' появляется слева от стрелки => и, следовательно, не выходит из ниоткуда. Понятно, что новая переменная типа, защищенная конструктором, не сигнализирует о неопределенности, как в «Nothing :: Maybe u', а также« Monad m => m u' просто отлично. – Ingo

+0

В какой-то момент вы говорите, что список никогда не вычисляется. Предположим, я был тем, кто писал 'rfib'. Давайте также предположим, что я беру один последний взгляд на тело 'rfib', чтобы подтвердить, что я написал, правильно, прежде чем перейти в подсказку ghci для проверки' rfib'. Теперь, не могли бы вы рассказать мне, какая часть информации заставила бы меня понять, что вычисление rfib никогда не сходится? – Jay

8

Результатом execState является значение состояния в конце вычисления. Но ваши вычисления никогда не заканчиваются: fib' звонки fib'. Хотя в вашем случае можно было бы доказать, что первые 10 элементов состояния в конечном итоге не изменяются, компилятор не может предсказать это (и не должен).

Возможно, вы захотите проверить Writer Monad, где вы можете обрабатывать письменные данные лениво.

+0

Я использую Control.Monad.State.Lazy. Несмотря на то, что фид «продолжается бесконечно, разве он не будет выполняться столько, сколько необходимо? Если я просто поставлю execState fib '[0,1] в строке, тогда я пойму, что вычисление может продолжаться вечно. Но не нужно брать 10 $ execState fib '[0, 1], чтобы получить достаточный результат, чтобы удовлетворить запрос, учитывая, что Haskell ленив? – Jay

+4

Lazyness не может делать волшебство. Даже когда 'fib' несколько раз повторяется, нет никакого способа узнать, что более поздний вызов' fib' может не указывать изменение на нечто совершенно другое. Обратите внимание, что вы всегда * устанавливаете * целое * состояние; факт, что вы случайно используете 'l ++ something', есть только специальность вашей конкретной программы. –

+0

@Jay, посмотрите на мой ответ, который расскажет вам, как вы могли знать, что он не закончится, даже не запустив его. – Ingo

4

return не работает, как в императивных языках. Она определяется по-разному для каждой монады, для состояния монады, она определяется как: return x = State $ \s -> (x,s)

Так что на самом деле не выйти из функции fib', но вместо этого связывает новые государственные монады к остальной части вычисления ,

EDIT:

Хотя мой первоначальный ответ, правда, он не отвечает на вопрос OP еще. В коде OP return действует как нет-op. По той причине, что код OP никогда не заканчивается, см. Другие ответы. Я до сих пор оставить свой ответ здесь, потому что я чувствую, что еще что-то важно отметить, учитывая, что return было показать излишне в образце кода в OP в

4
  1. return() линия является избыточным и может быть удалена
  2. (++) является почти всегда плохая идея, так как это O (N) в размере ее первого аргумента.
  3. Конструируется много списков возрастающей длины, в то время как ваша цель построить один бесконечный список
1

По предложению @Joachim Брайтнеру, в одно из возможных решений с отложенной Writer монады бы:

import Control.Monad.Writer 

fib' :: Writer [Int]() 
fib' = tell [1, 1] >> go 1 1 
    where 
     go :: Int -> Int -> Writer [Int]() 
     go f1 f2 = do 
      let f3 = f1 + f2 
      tell [f3] 
      go f2 f3 

main = print $ take 10 $ execWriter fib' 
Смежные вопросы