Haskell 具有多个 monad 类型的 do 子句
Haskell do clause with multiple monad types
我在 Haskell 中使用一个名为 Threepenny-GUI. In this library the main function returns a 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]
其中returns出现以下错误:
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
中。大多数时候,转换会为您准备好。例如,您可以有一个嵌套的 do
与 io
一起使用,然后仅在交互点转换它。
对于您的情况,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.
在幕后,do 只是 >>=(绑定)和 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
所以是的,只有 "supports" 一个 Monad
这更像是一个扩展评论 - 它没有解决主要问题,而是解决了您对 shufffle
的实施。它有 2 个问题:
- 您的实施效率低下 - O(n^2).
IO
不是适合它的类型 - shuffle 没有一般的副作用,它只需要一个随机源。
对于(1)有几种解法:一种是利用Seq
及其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得到O(n).
对于 (2),您只需要线程化随机生成器,而不是 IO
的全部功能。已经有不错的库 MonadRandom that defines a monad (and a type-class) for randomized computations. And another package already provides the shuffle
function。由于 IO
是 MonadRandom
的一个实例,您可以直接使用 shuffle
作为函数的替代。
我在 Haskell 中使用一个名为 Threepenny-GUI. In this library the main function returns a 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]
其中returns出现以下错误:
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
中。大多数时候,转换会为您准备好。例如,您可以有一个嵌套的 do
与 io
一起使用,然后仅在交互点转换它。
对于您的情况,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.
在幕后,do 只是 >>=(绑定)和 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
所以是的,只有 "supports" 一个 Monad
这更像是一个扩展评论 - 它没有解决主要问题,而是解决了您对 shufffle
的实施。它有 2 个问题:
- 您的实施效率低下 - O(n^2).
IO
不是适合它的类型 - shuffle 没有一般的副作用,它只需要一个随机源。
对于(1)有几种解法:一种是利用Seq
及其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得到O(n).
对于 (2),您只需要线程化随机生成器,而不是 IO
的全部功能。已经有不错的库 MonadRandom that defines a monad (and a type-class) for randomized computations. And another package already provides the shuffle
function。由于 IO
是 MonadRandom
的一个实例,您可以直接使用 shuffle
作为函数的替代。