Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 状态和错误monad堆栈,错误时状态回滚_Haskell_State Monad - Fatal编程技术网

Haskell 状态和错误monad堆栈,错误时状态回滚

Haskell 状态和错误monad堆栈,错误时状态回滚,haskell,state-monad,Haskell,State Monad,我有一个问题,我将通过以下示例加以说明: 假设我想做一些计算,在携带状态的同时,可以产生结果或错误。为此,我有以下monad堆栈: import Control.Monad.Trans.State ( get, modify, State ) import Control.Monad.Trans.Except ( catchE, throwE, ExceptT ) type MyMonad a = ExceptT String (State [Int]) a 因此,状态是一个整数列表,错误是

我有一个问题,我将通过以下示例加以说明:

假设我想做一些计算,在携带状态的同时,可以产生结果或错误。为此,我有以下monad堆栈:

import Control.Monad.Trans.State ( get, modify, State )
import Control.Monad.Trans.Except ( catchE, throwE, ExceptT )

type MyMonad a = ExceptT String (State [Int]) a
因此,状态是一个整数列表,错误是字符串,计算可以产生任何类型的“a”值。我可以做以下事情:

putNumber :: Int -> MyMonad ()
putNumber i = lift $ modify (i:)
现在,假设我定义了一个函数,将最后一个数字的一半添加到状态:

putHalf :: MyMonad ()
putHalf = do
  s <- lift get
  case s of
    (x:_) -> if even x then putNumber (div x 2) else throwE "Number can't be halved"
    [] -> throwE "The state is empty"
import Control.Monad.Trans
import Control.Monad.Trans.State
import Control.Monad.Trans.Except

type MyMonad' a = StateT [Int] (Except String) a

putNumber :: Int -> MyMonad' ()
putNumber i = modify (i:)

putHalf :: MyMonad' ()
putHalf = do
  s <- get
  case s of
    (x:_) -> if even x then putNumber (div x 2) else lift $ throwE "Number can't be halved"
    [] -> lift $ throwE "the state is empty"

putHalfTwice :: MyMonad' ()
putHalfTwice = putHalf >> putHalf

foo :: MyMonad' ()
foo = liftCatch catchE putHalfTwice (\_ -> putNumber 12)

main :: IO ()
main = do
  print $ runExcept (runStateT foo [2])
在这种情况下,如果由于任何原因,
putshalf
失败,数字12将添加到状态。到目前为止,一切都很好。但是,我可以定义一个调用
putshalf
两次的函数:

putHalfTwice :: MyMonad ()
putHalfTwice = putHalf >> putHalf
问题是,例如,如果状态仅包含数字2,则对
putshalf
的第一次调用将成功并修改状态,但第二次调用将失败。我需要两次
puthalftweep
来执行两次调用并修改状态两次,或者根本不修改状态并保持状态不变。我不能使用
catch
putWithAlternative
,因为状态在第一次调用中仍然被修改


我知道Parsec库是通过它的
try
操作符来实现的。我怎么能自己定义这些呢?是否有任何已经定义的monad transformer可以实现这一点?

如果在您的问题域中,故障永远不会改变状态,最简单的方法是反转层:

type MyMonad' a = StateT [Int] (Except String) a
您的原始单子同构于:

s -> (Either e a, s)
s -> Either e (a, s)
因此,无论成功与否,它总是返回一个新状态。此新单子同构于:

s -> (Either e a, s)
s -> Either e (a, s)
因此,它要么失败,要么返回新状态

以下程序从PutHalf中恢复两次,而不会损坏状态:

putHalf :: MyMonad ()
putHalf = do
  s <- lift get
  case s of
    (x:_) -> if even x then putNumber (div x 2) else throwE "Number can't be halved"
    [] -> throwE "The state is empty"
import Control.Monad.Trans
import Control.Monad.Trans.State
import Control.Monad.Trans.Except

type MyMonad' a = StateT [Int] (Except String) a

putNumber :: Int -> MyMonad' ()
putNumber i = modify (i:)

putHalf :: MyMonad' ()
putHalf = do
  s <- get
  case s of
    (x:_) -> if even x then putNumber (div x 2) else lift $ throwE "Number can't be halved"
    [] -> lift $ throwE "the state is empty"

putHalfTwice :: MyMonad' ()
putHalfTwice = putHalf >> putHalf

foo :: MyMonad' ()
foo = liftCatch catchE putHalfTwice (\_ -> putNumber 12)

main :: IO ()
main = do
  print $ runExcept (runStateT foo [2])
然后:

import Control.Monad.Trans
import Control.Monad.Trans.State
import Control.Monad.Trans.Except

type MyMonad a = ExceptT String (State [Int]) a

putNumber :: Int -> MyMonad ()
putNumber i = lift $ modify (i:)

putHalf :: MyMonad ()
putHalf = do
  s <- lift get
  case s of
    (x:_) -> if even x then putNumber (div x 2) else throwE "Number can't be halved"
    [] -> throwE "The state is empty"

putHalfTwice :: MyMonad ()
putHalfTwice = putHalf >> putHalf

try :: MyMonad a -> MyMonad a
try act = do
  s <- lift get
  act `catchE` (\e -> lift (put s) >> throwE e)

foo :: MyMonad ()
foo = putHalfTwice `catchE` (\_ -> putNumber 12)

bar :: MyMonad ()
bar = try putHalfTwice `catchE` (\_ -> putNumber 12)

main :: IO ()
main = do
  print $ runState (runExceptT foo) [2]
  print $ runState (runExceptT bar) [2]
import Control.Monad.Trans
进口管制.单子.跨州
进口管制.单子交易.例外
键入MyMonad a=ExceptT字符串(State[Int])a
putNumber::Int->MyMonad()
putNumber i=提升$modify(i:)
putHalf::MyMonad()
putHalf=do
如果是偶数x,则输入数字(div x 2),否则通过“数字不能减半”
[]->通过“状态为空”
PutHalf两次::MyMonad()
putHalf两次=putHalf>>putHalf
try::MyMonad a->MyMonad a
试着去做
s举(放s)>>throwE)
foo::MyMonad()
foo=putHalf两次'catch'(\ \ \ \->putNumber 12)
bar::MyMonad()
bar=尝试两次PutHalf'catch`(\\\->putNumber 12)
main::IO()
main=do
打印$runState(runexcepttfoo)[2]
打印$runState(runExceptT条)[2]

这很有效!我不确定这样实现
try
是否可以,因为我将状态的副本保存在内存中,但我认为没有其他方法。谢谢你,我有一个问题,为什么你要使用
liftCatch
而不仅仅是
lift
lift
就足以将表达式的类型
action1`catchE`action2
除了String
提升到
MyMonad
,但是参数
action1
action2
不会被取消——它们在
中仍然有类型,除了字符串
monad而不是
MyMonad
。这就是为什么各个转换器提供一个
liftCatch
函数,专门用于通过转换器提升
liftCatch
的参数和返回值。