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

时间:2015-06-22 13:20:19

标签: haskell monads threepenny-gui

我在Haskell中使用名为Threepenny-GUI的图形库。在这个库中,main函数返回一个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类型。

3 个答案:

答案 0 :(得分:8)

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

您可以转换操作,也可以将其嵌套在do中。大部分时间转换都将为您准备好。例如,您可以使用与do一起使用的嵌套io,然后仅在交互点进行转换。

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

  

liftIOLater :: IO () -> UI ()

     

安排稍后运行的IO操作。

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

  

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

     

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

答案 1 :(得分:5)

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

  1. 您的实施效率低下 - O(n ^ 2)
  2. IO不适合它 - shuffle没有一般的副作用,它只需要一个随机源。
  3. 对于(1),有几种解决方案:一种是使用Seq及其index,即 O(log n),这将使{{1} } O(n log n)。或者您可以使用ST arrays和其中一个standard algorithms来获取 O(n)

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

答案 2 :(得分:2)

在封面下,为&gt;&gt; =(绑定)做简单的语法糖,然后让:

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

所以,它只是&#34;支持&#34;一个Monad