Haskell中IO单子中元组的模式匹配

Haskell中IO单子中元组的模式匹配,haskell,pattern-matching,tuples,Haskell,Pattern Matching,Tuples,我一直在业余时间学习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,

我一直在业余时间学习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)之类的方法从元组中提取单个项,然后只指定一个值,但这显然是一种草率而迂回的处理方式。我觉得我可能是被一些语法上的细微差别绊倒了,而这些细微差别正盯着我看。如何解决这种类型的错误?应该可以直接匹配单子中封装的元组,不是吗?

我不知道为什么没有

rndPermu xs = hurf xs
但是要回答你提出的问题,试试这个

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

rndPermu xs=do(front,back)如果我正确理解了您要做的事情,
rndPermu
尝试获取
hurf
返回的
IO
中的值,并从中删除
IO
,如
rndPermu::IO a->a
。这是不可能的。
IO
monad中的返回值表示
hurf
函数使用IO,因此所有使用调用
hurf
结果的函数也将间接使用IO:它们的返回值也应该在
IO
monad中。这是由类型系统强制执行的

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

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

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

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

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

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

 where (front, back) = hurf xs

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

不,不。我知道IO单子是单向的,我读过的文章和书籍很早就把它记在了我的脑海中。我担心这有点模棱两可,所以我在发布后返回,将类型签名添加到rndPermu,以澄清我的意思是将其保留在IO中,尽管我认为GHCi会推断出这一点。我对从IO中删除受污染的值不感兴趣;我知道即使有可能,那也会很糟糕。具体来说,就是在我感到困惑的IO单子中,是否有可能对元组进行模式匹配。。。我在发帖后返回,将类型签名添加到rndPermu,以澄清我的意思是让它保留在IO中,尽管我认为GHCi无论如何都会推断出这一点。哦,但它确实推断出了类型。只是没能推断出你的意图。另外,正如其他人已经阐述的,您可以很好地将元组与IO monad(或任何monad)中的元组进行匹配;你不能用一种意味着你把价值从“一元家庭”中分离出来的方式来做这件事。就像我说的,这是人为的。毫无意义的“where”子句被附加在上面,因为这是我正在测试和好奇的东西。我天真地试图继承一个好的函数形式的刻板印象,尽量避免在我认为可以的地方出现命令式的“do”块,并且做一些类型系统不打算容纳的奇怪的事情。但这显然是更明智的方式。你的建议是正确的,对我有用。谢谢。这可能有道理,也可能没有道理,但我最初的想法并不是说我一定能在这样一种强类型语言中强制类型,而是在模式匹配时会出现某种多态性,单元组中的元组通常只是元组的子集,在该语句中根本不会有冲突。否则,我试图找到一些语法上有效的等价物:“where IO(front,back)=hurf xs”,希望模式本身能够以某种方式投射到IO单子中。我知道有点奇怪,但就是这样。单元组中的元组通常不是元组的子集。它通常是单子的一个子集。但它不一定是IO单子的一个子集。您可以对任何monad使用类似的构造。之所以
IO(front,back)=hurf xs
不起作用,是因为
IO
不是一个简单的数据构造函数。可能还有其他数据类型可以使用,但不是IO。只是为了增加重点。我想我现在明白了与其他答复的混淆。当您编写IO(前、后)=hurf xs时,您可能并不打算“打破”IO单子,但对于Haskell程序员来说,这正是您想要做的。
 where (front, back) = hurf xs