2012-03-15 84 views
14

我需要使用列表monad變壓器。我讀到ListT IOControl.Monad.List有潛在的問題,因爲IO是不可交換的,所以我在看ListT done right。但是我收到了一些意想不到的行爲。列表monad變壓器

考慮這個簡單的測試:

test = runListT $ do 
    x <- liftList [1..3] 
    liftIO $ print x 
    y <- liftList [6..8] 
    liftIO $ print (x,y) 

使用Control.Monad.List:

Main> test 
1 
(1,6) 
(1,7) 
(1,8) 
2 
(2,6) 
(2,7) 
(2,8) 
3 
(3,6) 
(3,7) 
(3,8) 
[(),(),(),(),(),(),(),(),()] 

使用 「ListT這樣做的權利」:

Main> test 
1 
(1,6) 

這是一個問題「 ListT做對了「,還是我只是用它錯了?有一個首選的替代方案嗎?

謝謝!

回答

8

這可能是對作者的部分內涵,因爲他們說

它可以讓列表中的每個元素都有其自身的副作用,只得到 `excecuted」如果列表中該元素真的檢查。

雖然我不確定。無論如何,你可以使用這個功能來排序整個 列表:

runAll_ :: (Monad m) => ListT m a -> m() 
runAll_ (ListT m) = runAll_' m where 
    runAll_' m = do 
     mm <- m 
     case mm of 
      MNil   -> return() 
      _ `MCons` mxs -> runAll_' mxs 

而且類似runAll返回一個列表應該很容易建造。

main = runAll_ $ do 
    x <- liftList [1..3] 
    liftIO $ print x 
    y <- liftList [6..8] 
    liftIO $ print (x,y) 

1 
(1,6) 
(1,7) 
(1,8) 
2 
(2,6) 
(2,7) 
(2,8) 
3 
(3,6) 
(3,7) 
(3,8) 
+0

嗯,好的這是有道理的,你的'runAll_'主意非常好!我期待的行爲類似於使用命令式語言的print語句的嵌套for循環。但是,如果「ListT做對了」是懶惰的,爲什麼它仍然在對列表頭進行副作用? – 2012-03-15 19:49:20

+0

假設你總是至少需要第一個元素,所以它將「整個列表」包裝在'm'中,並且將cdr包裝成'm';汽車沒有被包裹。如果你排列「整個列表」,它只會暴露汽車。 – Owen 2012-03-15 19:52:11