2011-11-18 57 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]