8

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

这是我的问题的一个例子。这是标准 main 函数的略微修改版本,如 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,但这会导致转换为UImonad 类型,而我实际上想继续使用该IO类型。

4

3 回答 3

8

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

您可以转换操作,也可以将其嵌套在do. 大多数时候,转换将为您准备好。例如,您可以有一个可以使用的嵌套doio然后仅在交互点进行转换。

在您的情况下,liftIOLaterThreePennyUI 包提供了一个函数来为您处理这个问题。

liftIOLater :: IO () -> UI ()

安排稍后运行的 IO 操作。

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

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

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

于 2015-06-22T13:29:38.253 回答
5

This is more an extended comment - it doesn't address the main question, but your implementation of shufffle. There are 2 issues with it:

  1. Your implementation is inefficient - O(n^2).
  2. IO isn't the right type for it - shuffle has no general side effects, it just needs a source of randomness.

For (1) there are several solutions: One is to use Seq and its index, which is O(log n), which would make shuffle O(n log n). Or you could use ST arrays and one of the standard algorithms to get O(n).

For (2), all you need is threading a random generator, not full power of IO. There is already nice library MonadRandom that defines a monad (and a type-class) for randomized computations. And another package already provides the shuffle function. Since IO is an instance of MonadRandom, you can just use shuffle directly as a replacement for your function.

于 2015-06-22T18:15:42.877 回答
2

在幕后,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

于 2015-06-22T17:15:13.183 回答