Как использовать Control.Monad.Cont в рекурсивной функции?

Я давал ответ на этот вопрос, и мне пришла идея использовать монаду Cont. Я недостаточно знаю Haskell, чтобы объяснить, почему эта программа не работает.

import Control.Monad.Cont

fib1 n = runCont (slow n) id
  where
    slow 0 = return 0
    slow 1 = return 1
    slow n = do
      a <- slow (n - 1)
      b <- slow (n - 2)
      return a + b

main = do
  putStrLn $ show $ fib1 10

Ошибка -

main.hs:10:18: error:
    • Occurs check: cannot construct the infinite type: a2 ~ m a2
    • In the second argument of ‘(+)’, namely ‘b’
      In a stmt of a 'do' block: return a + b
      In the expression:
        do a <- slow (n - 1)
           b <- slow (n - 2)
           return a + b
    • Relevant bindings include
        b :: a2 (bound at main.hs:9:7)
        a :: a2 (bound at main.hs:8:7)
        slow :: a1 -> m a2 (bound at main.hs:5:5)
   |
10 |       return a + b
   |   

Но это не имеет для меня смысла. Почему у меня a2 и m a2? Я ожидаю, что a и b будут одного типа.

Меня это беспокоит, потому что та же самая программа отлично работает в JavaScript. Может быть, для Haskell нужна подсказка типа?

const runCont = m => k =>
  m (k)

const _return = x =>
  k => k (x)
  
const slow = n =>
  n < 2
    ? _return (n)
    : slow (n - 1) (a =>
      slow (n - 2) (b =>
      _return (a + b)))
      
const fib = n =>
  runCont (slow(n)) (console.log)
  
fib (10) // 55


person Mulan    schedule 24.02.2019    source источник
comment
return a + b анализируется как (return a) + b. Вместо этого попробуйте return (a + b) или return $ a + b.   -  person Joseph Sible-Reinstate Monica    schedule 24.02.2019
comment
я знал, что это должно быть что-то простое :D   -  person Mulan    schedule 24.02.2019
comment
Я настоятельно рекомендую ставить сигнатуры типов (по крайней мере) для всех функций и привязок верхнего уровня. Это часто делает сообщения об ошибках типа значительно более понятными. Здесь я бы также рекомендовал сигнатуру типа для функции в блоке where. Это также упрощает (и ускоряет) чтение кода.   -  person David Young    schedule 24.02.2019


Ответы (1)


return a + b анализируется как (return a) + b, тогда как вы хотели return (a + b). Помните, что применение функции связывает сильнее, чем любой инфиксный оператор.

(Также принято писать return $ a + b, что означает одно и то же)

person Ben Millwood    schedule 24.02.2019
comment
почему люди голосуют за мой ответ, это скучно - person Ben Millwood; 24.02.2019
comment
Почему вы разместили ответ в первую очередь? - person chepner; 24.02.2019
comment
Если вопрос имеет тривиальное решение, на которое можно дать полный ответ в комментарии, я просто голосую за его закрытие, так как его больше нельзя воспроизвести. - person chepner; 24.02.2019
comment
Сначала я удалил вопрос, но потом увидел, что люди уже начали голосовать за этот ответ. Если этот ответ был полезен для других, я не хотел лишать его возможности будущих читателей, поэтому я восстановил его. Мои извинения, если вопрос был слишком простым. - person Mulan; 24.02.2019