我的递归列表构造有什么问题?

时间:2011-11-18 23:12:46

标签: list haskell monads

我简化了有问题的功能。我在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。我做错了什么?

3 个答案:

答案 0 :(得分:5)

luqui回答了你的问题,我会注意到一个有用的组合。

如果要对列表的所有元素执行monadic操作,请使用“mapM”。它被定义为:

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

或类似的东西。 [如果您了解其他一些组合器,可以使用mapMliftM2撰写foldr。]

 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]

答案 1 :(得分: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

答案 2 :(得分: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。它将函数应用于monadic计算中的“内部”值。)

哦,也是missno所说的: - )