Haskell使用多个monad类型来执行子句

我在Haskell中使用一个名为Threepenny-GUI的图形库。 在这个库中,主函数返回一个UI monad对象。 这使我非常头疼,因为当我尝试将IO值解压到局部变量时,我收到了抱怨不同monad类型的错误。

这是我的问题的一个例子。 这是标准主函数的稍微修改版本,如Threepenny-GUI的代码示例所示:

main :: IO ()
main = startGUI defaultConfig setup

setup :: Window -> UI ()
setup w = do

labelsAndValues <- shuffle [1..10]

shuffle :: [Int] -> IO [Int]
shuffle [] = return []
shuffle xs = do randomPosition <- getStdRandom (randomR (0, length xs - 1))
                let (left, (a:right)) = splitAt randomPosition xs
                fmap (a:) (shuffle (left ++ right))

请注意第五行:

labelsAndValues <- shuffle [1..10]

其中返回以下错误:

Couldn't match type ‘IO’ with ‘UI’
Expected type: UI [Int]
  Actual type: IO [Int]
In a stmt of a 'do' block: labelsAndValues <- shuffle [1 .. 10]

至于我的问题,我如何使用标准箭头符号( <- )解压IO函数,并继续将这些变量作为IO ()而不是UI () ,这样我就可以轻松地将它们传递给其他函数。

目前,我发现的唯一解决方案是使用liftIO ,但这会导致转换为UI monad类型,而我实际上仍想继续使用IO类型。


do块是针对特定类型的monad,不能只是在中间改变类型。

您可以转换的动作,或者你可以嵌套它里面do 。 大部分时间的转换将为你准备好。 例如,你可以有一个与io工作的嵌套的do ,然后只在交互点进行转换。

在你的情况下,通过ThreePennyUI包提供了一个liftIOLater函数来处理这个问题。

liftIOLater :: IO () -> UI ()

安排稍后运行的IO操作。

为了执行逆向转换,你可以使用runUI

runUI :: Window -> UI a -> IO a

在特定浏览器窗口中执行UI操作。 还运行所有计划的IO操作。


这更多的是一个扩展的评论 - 它没有解决主要问题,而是你实施shufffle 。 有两个问题:

  • 你的实现是低效的 - O(n ^ 2)。
  • IO不适合它 - shuffle没有一般的副作用,它只是需要一个随机性来源。
  • 对于(1)有几种解决方案:一种是使用Seq及其index ,即O(log n),这将使得shuffle O(n log n)。 或者你可以使用ST数组和一个标准算法来获得O(n)。

    对于(2),你所需要的只是一个随机生成器,而不是IO全部功能。 已经有很好的库MonadRandom为随机计算定义了monad(和一个类型)。 另一个软件包已经提供了shuffle功能。 由于IOMonadRandom一个实例, MonadRandom您可以直接使用shuffle替代您的函数。


    在封面下,do就是>> =(bind)的简单句法糖,并且让:

    do { x<-e; es } =   e >>= x -> do { es }
    do { e; es }    =   e >> do { es }
    do { e }        =   e
    do {let ds; es} =   let ds in do {es} 
    

    绑定的类型:

    (>>=) :: Monad m => a -> (a -> m b) -> m b
    

    所以是的,它只能“支持”一个Monad

    链接地址: http://www.djcxy.com/p/43369.html

    上一篇: Haskell do clause with multiple monad types

    下一篇: Haskell : (maybe) monad construction