2011-11-18 36 views
3

我簡化了所討論的功能。我在建立monad內的列表時遇到問題。我懷疑有一個優先問題。我的遞歸列表構造有什麼問題?

newtype Boundary = MkBoundary Integer 

testFunc :: [Boundary] -> [Maybe Integer] 
testFunc (MkBoundary x:xs) 
    | (even x) = Just x : testFunc xs 
    | otherwise = Nothing : testFunc xs 
testFunc _ = [] 

這個按預期工作。但我需要在monad中工作。我將在此示例中使用IO

testFunc :: [Boundary] -> IO [Maybe Integer] 
testFunc (MkBoundary x:xs) 
    | (even x) = return $ Just x : testFunc xs 
    | otherwise = return $ Nothing : testFunc xs 
testFunc _ = [] 

不管我如何操作優先級,都會中斷。

test.hs:6:35: 
    Couldn't match expected type `[Maybe Integer]' 
       with actual type `IO [Maybe Integer]' 
    In the return type of a call of `testFunc' 
    In the second argument of `(:)', namely `testFunc xs' 
    In the second argument of `($)', namely `Just x : testFunc xs' 
Failed, modules loaded: none. 

我想完成的是構建一個列表,然後將其返回給IO。我究竟做錯了什麼?

回答

4

的問題是,testFunc xs返回IO [Maybe Integer],並且您正在使用它作爲一個列表的尾部,好像它是一個[Maybe Integer]。你需要提取:

| (even x) = do 
    xs' <- testFunc xs 
    -- now xs' is of type [Maybe Integer] 
    return $ Just x : xs' 

或者說同樣的事情更簡潔的方式:

| (even x) = (Just x :) <$> testFunc xs 

(<$>)Control.Applicative,且類型

(<$>) :: (a -> b) -> IO a -> IO b 

專門用於IO。它將函數應用於單值計算中的「內部」值。)

哦,亦是missingno說:-)

4

你忘了更改第二種情況

test_func _ = return [] 
      -- ^^^^^^ 

另外,我覺得你的榜樣作用,可以更清楚地寫爲

test_func :: [Boundary] -> [Maybe Integer] 
test_func = ... 

monadic_test_func = [Boundary] -> IO [Maybe Integer] 
monadic_test_func = return . test_func 

這使得從討厭的分離純代碼monad的東西。它還可以讓您不必輸入「返回」三次! :)


最後,爲什麼你在第一個地方創建這樣的功能? monad部分(至少在你的例子中)似乎與主要功能邏輯有些不相關(因爲你只是在做return)。

也許你使用一些不錯的庫函數來保持你的函數是純粹的和未觸及的?

--instead of 
monadic_value >>= monadic_test_func 

--use 
fmap test_func monadic_value 
-- or equivalently 
test_func <$> monadic_value 
liftM test_func monadic_value 
+0

他說,他已經簡化了問題的功能。有可能他需要在某個地方執行此功能的I/O操作。並且學習如何將純代碼轉換爲單代碼在任何情況下都是非常有用的技巧。 – luqui

+0

噢,但是我也可以看到他是怎麼會有一種誤解,認爲如果他需要在monad中使用它,他需要重寫他完美的純函數,在這種情況下,這個答案是正確的。爲清晰起見編輯 – luqui

+0

。 – hugomg

5

luqui回答你的問題,我會注意到一個有用的組合子。

如果要對列表中的所有元素執行monadic操作,請使用「mapM」。它定義爲:

mapM f [] = return [] 
mapM f (x:xs) = do y <- f x 
        ys <- mapM f xs 
        return (y:ys) 

或其他等效物。 [如果您知道其他一些組合器,則可以使用liftM2foldr編寫mapM。]

testFunc = mapM f 
    where f (MkBoundary x) | even x = do print x 
              return $ Just x 
          | otherwise = return Nothing 

試驗GHCI:

*Main> testFunc [MkBoundary 2, MkBoundary 3, MkBoundary 4] 
2 
4 
[Just 2,Nothing,Just 4]