我在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
类型。
答案 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
的实施。它有两个问题:
IO
不适合它 - shuffle没有一般的副作用,它只需要一个随机源。对于(1),有几种解决方案:一种是使用Seq
及其index
,即 O(log n),这将使{{1} } O(n log n)。或者您可以使用ST
arrays和其中一个standard algorithms来获取 O(n)。
对于(2),您只需要线程化随机生成器,而不是shuffle
的全部功能。已经很好的库MonadRandom为随机计算定义了monad(和类型类)。另一个包已提供shuffle
function。由于IO
是IO
的一个实例,因此您可以直接使用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