Haskell使用多个monad类型来执行子句
我在Haskell中使用一个名为Threepenny-GUI的图形库。 在这个库中,主函数返回一个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
类型。
do
块是针对特定类型的monad,不能只是在中间改变类型。
您可以转换的动作,或者你可以嵌套它里面do
。 大部分时间的转换将为你准备好。 例如,你可以有一个与io
工作的嵌套的do
,然后只在交互点进行转换。
在你的情况下,通过ThreePennyUI包提供了一个liftIOLater
函数来处理这个问题。
liftIOLater :: IO () -> UI ()
安排稍后运行的IO操作。
为了执行逆向转换,你可以使用runUI
:
runUI :: Window -> UI a -> IO a
在特定浏览器窗口中执行UI操作。 还运行所有计划的IO操作。
这更多的是一个扩展的评论 - 它没有解决主要问题,而是你实施shufffle
。 有两个问题:
IO
不适合它 - shuffle没有一般的副作用,它只是需要一个随机性来源。 对于(1)有几种解决方案:一种是使用Seq
及其index
,即O(log n),这将使得shuffle
O(n log n)。 或者你可以使用ST
数组和一个标准算法来获得O(n)。
对于(2),你所需要的只是一个随机生成器,而不是IO
全部功能。 已经有很好的库MonadRandom为随机计算定义了monad(和一个类型)。 另一个软件包已经提供了shuffle
功能。 由于IO
是MonadRandom
一个实例, MonadRandom
您可以直接使用shuffle
替代您的函数。
在封面下,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
链接地址: http://www.djcxy.com/p/43369.html