2014-10-29 4 views
2

Я могу написать следующее:Взаимно рекурсивные определения IO

f :: [Int] -> [Int] 
f x = 0:(map (+1) x) 

g :: [Int] -> [Int] 
g x = map (*2) x 

a = f b 
b = g a 

main = print $ take 5 a 

И все работает прекрасно (ideone).

Однако, позволяет сказать, что я хочу g сделать нечто более сложное, чем умножить на 2, как попросить пользователя число и добавить, что, например, так:

g2 :: [Int] -> IO [Int] 
g2 = mapM (\x -> getLine >>= (return . (+x) . read)) 

Как я тогда, ну, галстук узел?

Разъяснение:

В основном я хочу список Int с от f быть вход g2 и список Int с от g2 быть вход f.

+0

Возможно, вы захотите посмотреть 'mfix' с' Control.Monad.Fix'. – dfeuer

+0

Можете ли вы попытаться уточнить, что именно * вы пытаетесь сделать? – dfeuer

+0

Именно в качестве примера, за исключением замены 'g' на' g2'. – Clinton

ответ

11

effectful обобщение списков ListT:

import Control.Monad 
import Pipes 

f :: ListT IO Int -> ListT IO Int 
f x = return 0 `mplus` fmap (+ 1) x 

g2 :: ListT IO Int -> ListT IO Int 
g2 x = do 
    n <- x 
    n' <- lift (fmap read getLine) 
    return (n' + n) 

a = f b 
b = g2 a 

main = runListT $ do 
    n <- a 
    lift (print n) 
    mzero 

Вы также можете реализовать take как функциональность с небольшим дополнительным кодом:

import qualified Pipes.Prelude as Pipes 

take' :: Monad m => Int -> ListT m a -> ListT m a 
take' n l = Select (enumerate l >-> Pipes.take n) 

main = runListT $ do 
    n <- take' 5 a 
    lift (print n) 
    mzero 

Пример сеанса:

>>> main 
0 
1<Enter> 
2 
2<Enter> 
3<Enter> 
7 
4<Enter> 
5<Enter> 
6<Enter> 
18 
7<Enter> 
8<Enter> 
9<Enter> 
10<Enter> 
38 

You можно узнать больше о ListT b y reading the pipes tutorial, в частности section on ListT.