Haskell 具有不同中断类型的有状态循环
我正在尝试将以下有状态命令代码转换为HaskellHaskell 具有不同中断类型的有状态循环,haskell,monads,monad-transformers,state-monad,Haskell,Monads,Monad Transformers,State Monad,我正在尝试将以下有状态命令代码转换为Haskell while (true) { while (get()) { if (put1()) { failImmediately(); } } if (put2()) { succeedImmediately(); } } put1和put2都读取并修改系统的状态获取为简单起见,只需读取状态即可立即失败应打破无休止的循环并呈现一种结果,立即成功也应打破循环,但呈现不同的结果 我尝试使用的是State
while (true) {
while (get()) {
if (put1()) {
failImmediately();
}
}
if (put2()) {
succeedImmediately();
}
}
put1
和put2
都读取并修改系统的状态<代码>获取为简单起见,只需读取状态即可<代码>立即失败应打破无休止的循环并呈现一种结果,立即成功
也应打破循环,但呈现不同的结果
我尝试使用的是State Env Result
,其中Env
表示环境的状态,Result
类似于对于某些自定义的故障
和成功
我很难接受这样一个要求,即一旦其中一个表达式被生成(打破循环),整个结果表达式就应该崩溃为失败
/成功
,否则继续运行
我的一个想法是使用orbit Exit()
其中data Exit=Success | Failure
并使用StateT
以某种方式对orbit
的Left
进行操作,就好像orbit
是被链接的单子,即忽略任何后续操作
我非常感谢haskell代码的任何灵感或示例能够实现与上面代码片段相同的行为
编辑:精练版移到了一个单独的问题“一个几乎是字面的、不优雅但有效的翻译
我们利用ContT
monad转换器来实现
“提前返回”。也就是说,我们希望能够在任何一点打破我们的循环。这是通过使用callCC$\exit->…
实现的,这大致使exit
成为我们的神奇功能,让我们立即从内部块中逃脱
import Control.Monad.Cont
action :: IO String
action = flip runContT return $ callCC $ \exit ->
forever $ do -- while (true)
let loop = do
r1 <- lift $ get -- if (get())
when r1 $ do
r2 <- lift $ put1
when r2 $ -- if (put1())
exit "failImmediately"
loop -- "repeat while"
loop
r3 <- lift $ put2
when r3 $
exit "succeedImmediately"
get :: IO Bool
get = readLn
put1 :: IO Bool
put1 = putStrLn "put1 here" >> readLn
put2 :: IO Bool
put2 = putStrLn "put2 here" >> readLn
main :: IO ()
main = action >>= putStrLn
import Control.Monad.Cont
操作::IO字符串
action=flip runContT return$callCC$\exit->
永远$do--while(真)
让循环=做
r1>readLn
main::IO()
main=操作>>=putStrLn
我们还可以定义一些自定义帮助程序来美化代码:
action2 :: IO String
action2 = flip runContT return $ callCC $ \exit ->
forever $ do -- while (true)
whileM get $ -- while(get())
whenM put1 $ -- if (put1())
exit "failImmediately"
whenM put2 $ -- if (put2())
exit "succeedImmediately"
whenM :: (MonadTrans t, Monad m, Monad (t m)) => m Bool -> t m () -> t m ()
whenM condition a = do
r <- lift condition
when r a
whileM :: (MonadTrans t, Monad m, Monad (t m)) => m Bool -> t m () -> t m ()
whileM condition a = whenM condition (a >> whileM condition a)
action2::IO字符串
action2=flip runContT return$callCC$\exit->
永远$do--while(真)
whileM get$--while(get())
whenM put1$--if(put1())
退出“立即失效”
whenM put2$--if(put2())
退出“立即成功”
whenM::(单子trans t,单子m,单子(tm))=>m Bool->tm()->tm()
当工况a=do时
r m Bool->t m()->t m()
whileM条件a=whenM条件(a>>whileM条件a)
使用@chi答案中的工具包,只需强调您不需要ContT的全部功能,的直接短路语义就足够了:
import Control.Monad.Trans.Either
data Result a = Failure | Success a
foo :: EitherT (Result Int) IO Int
foo = forever $ do
whileM get $ do
whenM put1 $ do
left Failure
whenM put2 $ do
left $ Success 42
run :: (Monad m) => EitherT (Result a) m a -> m (Maybe a)
run act = do
res <- runEitherT act
return $ case res of
Left Failure -> Nothing
Left (Success x) -> Just x
Right x -> Just x
-- whenM / whileM and get/put1/put2 as per @chi's answeer
import Control.Monad.Trans
数据结果a=失败|成功a
foo::EitherT(结果Int)IO Int
foo=永远$do
当我得到$do时
什么时候付1美元
左失败
什么时候付2美元
剩下42美元
运行::(单子m)=>EitherT(结果a)MA->m(可能是a)
做某事
没什么
左(成功x)->仅x
右x->仅x
--whenM/whileM和get/put1/put2根据@chi的回答
您应该查看。如果这个暗示还不够,请告诉我,你需要更多的细节:)我感觉这可能是我需要的,只是我一点也不知道如何在这个场景中使用它:(.如果你能详细说明,我将非常感激。谢谢你,我希望有一种方法可以让所有的控制流像东西一样(当)可以隐藏在某个抽象中(可能/可能是单子隐藏)另外,这两个结果的类型也不同,本例假设这两个结果都是IO字符串。我希望整个过程返回成功或失败,或者永远不会终止。如果结果的类型不同,您可以使用或A B
而不是字符串
,这样您就可以在一些Left/Ri之后将其用作公共类型ght
wrapping。您可能已经意识到了这一点。也许有更优雅的方法可以使用某个库中的某个巧妙的循环组合器来抽象所有内容。但是,该代码非常有命令性和状态性:每一行都会以某种方式影响状态。命令性代码段也非常简单——我不知道我们是否能在这方面击败它清晰性方面。也许其他人会提供更好的解决方案。@Jakubdian我也意识到你只需要状态
而不需要IO
。我同意--ContT
有点过火了。谢谢,这似乎解决了我最初的问题(当我用状态替换IO时)。考虑到“编辑”这一点,难道不能更简单一点吗关于我的原始帖子?可能有一个基于MonadPlus的解决方案…可能值得一个单独的问题(问题范围缩小)