gpt4 book ai didi

haskell - 具有不同类型短路的状态计算(也许,或者)

转载 作者:行者123 更新时间:2023-12-02 11:23:22 25 4
gpt4 key购买 nike

我正在尝试找到将以下有状态命令式代码转换为纯函数表示的最优雅的方法(最好在 Haskell 中使用其 Monad 实现提供的抽象)。然而,我还不擅长使用变压器等组合不同的单子(monad)。在我看来,在学习如何自己完成此类任务时,分析他人对此类任务的看法最有帮助。命令式代码:

while (true) {
while (x = get()) { // Think of this as returning Maybe something
put1(x) // may exit and present some failure representation
}
put2() // may exit and present some success representation
}

何时 get返回Nothing我们需要继续执行 put2 ,当 get返回Just x我们想要 x传递给put1并且仅当 put1 时才短路否则失败或循环。基本上put1put2可能会终止整个事情或移动到以下语句以某种方式改变底层状态。 get可以成功并调用 put1并循环或失败并继续put2 .

我的想法是这样的:

forever $ do
forever (get >>= put1)
put2

我为什么要寻找类似的东西是因为 (get >>= put1)只要 get 就可以简单地短路没有任何可返回的内容或 put1终止。同样put2终止外循环。但是我不知道如何混合 State与必要的Maybe和/或 Either来实现这一目标。

我认为使用变压器来组合 State而其他 monad 是必要的,因此代码很可能不会那么简洁。但我想情况也可能不会更糟。

欢迎提出如何优雅地实现翻译的任何建议。这与“Stateful loop with different types of breaks ”不同,避免使用 if 显式控制流。 , when , while而是试图鼓励使用 Maybe , Either ,或其他一些方便的>>=语义。此外,总有一种直接的方法可以将代码转换为功能性代码,但它很难被认为是优雅的。

最佳答案

您正在寻找 EitherT ExceptT 。它添加了两种返回变压器堆栈的方法。计算可以是 return athrowError e 。错误和返回之间有两个区别。错误保留在 Left 上并返回 Right 。当你 >>=出现错误时会短路。

newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }

return :: a -> EitherT e m a
return a = EitherT $ return (Right a)

throwError :: e -> EitherT e m a
throwError e = EitherT $ return (Left a)

我们还将使用名称 left = throwErrorright = return .

Left 上的错误不要继续,我们将使用它们来表示退出循环。我们将使用类型EitherT r m ()表示一个循环,该循环要么因破坏结果而停止 Left r或继续 Right () 。这几乎完全是forever ,除非我们打开 EitherT并摆脱 Left围绕返回值。

import Control.Monad
import Control.Monad.Trans.Either

untilLeft :: Monad m => EitherT r m () -> m r
untilLeft = liftM (either id id) . runEitherT . forever

在充实您的示例后,我们将回到如何使用这些循环。

由于您希望看到几乎所有逻辑都消失,因此我们将使用 EitherT对于其他一切也是如此。获取数据的计算是 Done或返回数据。

import Control.Monad.Trans.Class
import Control.Monad.Trans.State

data Done = Done deriving Show

-- Gets numbers for a while.
get1 :: EitherT Done (State Int) Int
get1 = do
x <- lift get
lift . put $ x + 1
if x `mod` 3 == 0
then left Done
else right x

放置数据的第一个计算是 Failure或返回。

data Failure = Failure deriving Show

put1 :: Int -> EitherT Failure (State Int) ()
put1 x = if x `mod` 16 == 0
then left Failure
else right ()

放置数据的第二个计算是 Success或返回。

data Success = Success deriving Show

put2 :: EitherT Success (State Int) ()
put2 = do
x <- lift get
if x `mod` 25 == 0
then left Success
else right ()

对于您的示例,我们需要组合两个或多个计算,这些计算都以不同的方式异常停止。我们将用两个嵌套 EitherT 来表示这一点s。

EitherT o (EitherT i m) r

外层EitherT是我们目前正在运营的一个。我们可以转换 EitherT o m aEitherT o (EitherT i m) a通过添加额外的 EitherT每一层 m

over :: (MonadTrans t, Monad m) => EitherT e m a -> EitherT e (t m) a
over = mapEitherT lift

内在EitherT层将像变压器堆栈中的任何其他底层单子(monad)一样被对待。我们可以lift一个EitherT i m aEitherT o (EitherT i m) a

我们现在可以构建一个要么成功要么失败的整体计算。会破坏当前循环的计算被操作 over 。会破坏外循环的计算是 lift编辑。

example :: EitherT Failure (State Int) Success
example =
untilLeft $ do
lift . untilLeft $ over get1 >>= lift . put1
over put2

总体Failurelift ed 两次进入最内循环。这个例子很有趣,可以看到一些不同的结果。

main = print . map (runState $ runEitherT example) $ [1..30]
<小时/>

如果 EitherT有一个 MFunctor 例如,over只是 hoist lift ,这是一种经常使用的模式,值得拥有一个经过深思熟虑的名字。顺便说一句,我用EitherT超过ExceptT主要是因为它的名称加载较少。无论哪一个提供 MFunctor对我来说,实例首先将最终胜出作为任一 monad 转换器。

关于haskell - 具有不同类型短路的状态计算(也许,或者),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32398321/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com