具有多个monad类型的Haskell do子句

von*_*dip 8 monads haskell threepenny-gui

我在Haskell中使用一个名为Threepenny-GUI的图形库.在这个库中,main函数返回一个UImonad对象.这让我很头疼,因为当我尝试将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))
Run Code Online (Sandbox Code Playgroud)

请注意第五行:

labelsAndValues <- shuffle [1..10]
Run Code Online (Sandbox Code Playgroud)

返回以下错误:

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]
Run Code Online (Sandbox Code Playgroud)

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

目前,我找到的唯一解决方案是使用liftIO,但这会导致转换为UImonad类型,而我实际上希望继续使用该IO类型.

Ben*_*aum 8

一个do块用于特定类型的monad,您不能只在中间更改类型.

您可以转换操作,也可以将其嵌入其中do.大部分时间转换都将为您准备好.例如,您可以使用嵌套do,io然后仅在交互点转换它.

在您的情况下,liftIOLater提供了一个函数来通过ThreePennyUI包为您处理此问题.

liftIOLater :: IO () -> UI ()

安排稍后运行的IO操作.

要执行逆向转换,您可以使用runUI:

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

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


Pet*_*lák 5

这是一个扩展的注释-它没有解决主要问题,而是您对的实现shufffle。有两个问题:

  1. 您的实现效率很低-O(n ^ 2)
  2. IO 不是正确的类型-随机播放没有一般的副作用,它只需要随机性即可。

对于(1),有几种解决方案:一种是使用Seq及其index,它是O(log n),这将使shuffle O(n log n)。或者,您可以使用数组标准算法之一来获取O(n)ST

对于(2),您需要的是对一个随机数生成器进行线程处理,而不是的全功能IO。已经有不错的库MonadRandom,它为随机计算定义了monad(和类型类)。并且另一个软件包已经提供了该shuffle功能。由于IO是的实例MonadRandom,因此您可以shuffle直接将其用作功能的替代品。