3

我在业余时间一直在研究 Haskell,最近涉足单子函数领域。我已经将我一直在做的练习中的代码提取到这个非常人为的示例中,以隔离我遇到的确切问题:

import System.Random

rndPermu :: [a] -> IO (a, [a])
rndPermu xs = (front, back)
    where (front, back) = hurf xs

hurf :: [a] -> IO (a, [a])
hurf xs = randomRIO (0, (length xs) - 1) >>= \r -> return $ removeAt r xs

removeAt :: Int -> [a] -> (a, [a])
removeAt n xs = (e, rest)
    where e    = xs !! n
          rest = take n xs ++ (tail $ drop n xs)

rndPermu 在加载到 GHCi 时会产生类型错误,说明在“where”子句中需要类型 (t, t1) 但收到了 IO (a, [a])。我可以使用 (liftM fst) 之类的东西从元组中提取单个项目并只分配一个值,但这显然是一种草率而迂回的处理方式。我觉得我可能会因为一些细微的语法细微差别而绊倒在我脸上。如何解决这种类型的错误?应该可以直接匹配一个包裹在单子中的元组,不是吗?

4

4 回答 4

2

我不知道你为什么没有

rndPermu xs = hurf xs

但要回答你问的问题,试试这个

rndPermu xs = do (front, back) <- hurf xs
                 return (front, back)

我的理解是你不能直接匹配IO. 您必须先使用<-语法提取它。

于 2009-12-28T19:24:09.523 回答
2

如果我正确理解您要执行的操作,请rndPermu尝试获取IO返回的值hurf并从中删除IO,例如rndPermu :: IO a -> a. 这是不可能的。monad中的返回值IO表明该hurf函数使用 IO,因此所有使用调用结果的函数也hurf将间接使用 IO:它们的返回值也应该在IOmonad 中。这是由类型系统强制执行的。

如果您只想在 monad 中使用模式匹配,最直接的方法是使用 do-notation:

rndPermu :: [a] -> IO (a, [a])
rndPermu xs =
   do (front, back) <- hurf xs
      return (front, back)

一种常见的模式是使用不同的纯函数对值进行进一步处理。这些函数只是从IO或不同的 monad 调用的,但它们不需要知道这一点:

-- pure function to do something with the result of |hurf|
modify :: (a, [a]) -> (a, [a])
modify (a, as) = (a, reverse as)

rndPermu :: [a] -> IO (a, [a])
rndPermu xs =
   do r <- hurf xs
      return (modify r)
-- or, with >>= operator:
-- hurf xs >>= return . modify
于 2009-12-28T19:37:06.440 回答
1

作为do块的替代方案,您可以在绑定一元值的函数中进行模式匹配:

rndPermu xs = hurf xs >>= \(front, back) -> return (front, back)

rndPermu xs = hurf xs >>= \res -> case res of (front, back) -> return (front, back)
于 2009-12-29T14:14:08.560 回答
0

为了回答您评论中的问题,GHCi确实推断rndPermu应该具有 IO 类型。那不是问题。问题是这一行:

 where (front, back) = hurf xs

类型推断只是(松散地)意味着您不必指定您正在处理的表达式的类型。类型推断并不意味着 Haskell 会简单地将一种类型的值默默地转换为另一种类型;事实上恰恰相反。正如其他人所提到的,do如果您不想编写块,则不必编写块,但您必须处理具有 IO 值的事实。

于 2009-12-29T17:23:36.507 回答