【问题标题】:Haskell do clause with multiple monad types具有多种单子类型的 Haskell do 子句
【发布时间】:2015-06-22 13:20:18
【问题描述】:

我在 Haskell 中使用了一个名为 Threepenny-GUI 的图形库。在这个库中,主函数返回一个UI monad 对象。这让我很头疼,因为当我尝试将 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]

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

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

【问题讨论】:

  • 你不能在 do 块中途改变 monad
  • 你想继续使用IO类型是什么意思? liftIO $ do ... a block of IO code ... 会是你要找的吗?
  • 根据这个线程的结论,我提出了一个关于如何集成打印的新问题。任何帮助将不胜感激 - stackoverflow.com/questions/30988595/…
  • @vondip 你能说一下你是如何使用 liftIO 转换为 UI monad 的吗?我也有类似的问题:stackoverflow.com/questions/51906879/…

标签: haskell monads threepenny-gui


【解决方案1】:

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

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

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

liftIOLater :: IO () -&gt; UI ()

安排稍后运行的 IO 操作。

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

runUI :: Window -&gt; UI a -&gt; IO a

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

【讨论】:

  • 在我的情况下,我正在尝试使用一些随机 int 列表,然后将其显示到屏幕上,目前尚不清楚如何将 io 操作分开以在稍后阶段执行
  • UIMonadIO的一个实例,所以liftIO足以处理OP的问题(参见the answer to the follow-up question)。 liftIOLater 仅用于少数更棘手的情况(例如,只有在设置了 GUI 的其余部分后才应在 GUI 初始化期间运行的操作)。
【解决方案2】:

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

  1. 您的实现效率低下 - O(n^2)
  2. IO 不适合它 - shuffle 没有一般的副作用,它只需要一个随机源。

对于(1)有几种解决方案:一种是使用Seq及其index,即O(log n),这将使shuffleO (n log n)。或者您可以使用ST arraysstandard algorithms 之一来获得O(n)

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

【讨论】:

    【解决方案3】:

    实际上,do 只是 >>= (bind) 和 let 的语法糖:

    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-09-08
      • 1970-01-01
      • 1970-01-01
      • 2014-10-04
      • 2021-03-07
      • 2017-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多