我正在 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]
关于我的问题,如何解压 IO
使用标准箭头符号( <-
)的函数,并继续将这些变量设置为 IO ()
而不是UI ()
,这样我就可以轻松地将它们传递给其他函数。
目前,我找到的唯一解决方案是使用 liftIO
,但这会导致转换为 UI
monad 类型,而我实际上想继续使用 IO
类型。
最佳答案
do
block 适用于特定类型的 monad,您不能只更改中间的类型。
您可以转换操作,也可以将其嵌套在 do
中。大多数时候,转变已经为你准备好了。例如,您可以拥有一个与 io
配合使用的嵌套 do
,然后仅在交互时对其进行转换。
就您而言,ThreePennyUI 包提供了一个 liftIOLater
函数来为您处理此问题。
liftIOLater :: IO () -> UI ()
Schedule an IO action to be run later.
为了执行相反的转换,您可以使用runUI
:
runUI :: Window -> UI a -> IO a
Execute an UI action in a particular browser window. Also runs all scheduled IO action.
关于具有多种 monad 类型的 Haskell do 子句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30981044/