我在业余时间一直在研究Haskell,最近进入了monadic功能领域。我已经从我正在研究这个非常人为的例子的练习中提炼出代码,以找出我遇到的确切问题:
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)这样的东西来从元组中提取单个项目,只需分配一个值,但这显然是一种草率和迂回的方式来处理事情。我觉得我可能会磕磕绊绊地看着面对我的语法的一些细微差别。如何解决此类型错误?应该可以直接匹配包含在monad中的元组,不应该吗?
答案 0 :(得分:2)
我不知道为什么你没有
rndPermu xs = hurf xs
但要回答你问的问题,试试这个
rndPermu xs = do (front, back) <- hurf xs
return (front, back)
我的理解是你无法直接匹配IO
内的某些内容。您必须先使用<-
语法提取它。
答案 1 :(得分:2)
如果我正确理解您要执行的操作,rndPermu
会尝试获取IO
返回的hurf
中的值,并从中移除IO
,例如{ {1}}。这是不可能的。 rndPermu :: IO a -> a
monad中的返回值表示IO
函数使用IO,所有使用hurf
调用结果的函数都会间接使用IO:它们的返回值也应该在hurf
monad。这是由类型系统强制执行的。
如果你只想在monad中使用模式匹配,最直接的方法是使用do-notation:
IO
一种常见的模式是使用不同的纯函数来进一步处理值。
这些函数只是从rndPermu :: [a] -> IO (a, [a])
rndPermu xs =
do (front, back) <- hurf xs
return (front, back)
或不同的monad调用,但他们不需要知道:
IO
答案 2 :(得分:1)
作为do
块的替代方法,您可以在绑定monadic值的函数中进行模式匹配:
rndPermu xs = hurf xs >>= \(front, back) -> return (front, back)
rndPermu xs = hurf xs >>= \res -> case res of (front, back) -> return (front, back)
答案 3 :(得分:0)
要回答评论中的问题,GHCi 推断rndPermu
应该有IO类型。那不是问题。问题是这一行:
where (front, back) = hurf xs
类型推断只是(松散地)意味着您不必指定要处理的表达式的类型。类型推断并不意味着Haskell只是默默地将一种类型的值转换为另一种类型的值;事实恰恰相反。正如其他人提到的,如果你不想写,你不必编写do
块,但你必须处理你有IO值的事实。