Haskell 中的嵌套状态
Nested States in Haskell
我正在尝试定义一系列状态机,这些状态机具有不同类型的状态。特别是,更多 "complex" 状态机具有通过组合更简单状态机的状态而形成的状态。
(这类似于面向对象的设置,其中一个对象具有多个属性,这些属性也是对象。)
这是我想要实现的目标的简化示例。
data InnerState = MkInnerState { _innerVal :: Int }
data OuterState = MkOuterState { _outerTrigger :: Bool, _inner :: InnerState }
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = do
i <- _innerVal <$> get
put $ MkInnerState (i + 1)
return i
outerStateFoo :: Monad m => StateT OuterState m Int
outerStateFoo = do
b <- _outerTrigger <$> get
if b
then
undefined
-- Here I want to "invoke" innerStateFoo
-- which should work/mutate things
-- "as expected" without
-- having to know about the outerState it
-- is wrapped in
else
return 666
更一般地说,我想要一个通用框架,其中这些嵌套更复杂。这是我想知道如何做的事情。
class LegalState s
data StateLess
data StateWithTrigger where
StateWithTrigger :: LegalState s => Bool -- if this trigger is `True`, I want to use
-> s -- this state machine
-> StateWithTrigger
data CombinedState where
CombinedState :: LegalState s => [s] -- Here is a list of state machines.
-> CombinedState -- The combinedstate state machine runs each of them
instance LegalState StateLess
instance LegalState StateWithTrigger
instance LegalState CombinedState
liftToTrigger :: Monad m, LegalState s => StateT s m o -> StateT StateWithTrigger m o
liftToCombine :: Monad m, LegalState s => [StateT s m o] -> StateT CombinedState m o
就上下文而言,这就是我想用这台机器实现的目标:
我想设计这些叫做 "Stream Transformers" 的东西,它们基本上是有状态的函数:它们消耗一个令牌,改变它们的内部状态并输出一些东西。具体来说,我对输出为布尔值的 class 流转换器感兴趣;我们将这些称为 "monitors".
现在,我正在尝试为这些对象设计组合器。其中一些是:
- 一个
pre
组合子。假设 mon
是一个监视器。然后,pre mon
是一个监视器,它总是在第一个令牌被消耗后生成 False
,然后模仿 mon
的行为,就好像现在正在插入前一个令牌一样。我想在上面的示例中使用 StateWithTrigger
对 pre mon
的状态进行建模,因为新状态是一个布尔值以及原始状态。
- 一个
and
组合器。假设 m1
和 m2
是监视器。然后,m1 `and` m2
是一个监视器,它将令牌馈送到 m1,然后馈送到 m2,如果两个答案都为真,则生成 True
。我想在上面的示例中使用 CombinedState
对 m1 `and` m2
的状态进行建模,因为必须维护两个监视器的状态。
For context, this is what I want to achieve with this machinery:
I want to design these things called "Stream Transformers", which are basically stateful functions: They consume a token, mutate their internal state and output something. Specifically, I am interested in a class of Stream Transformers where the output is a Boolean value; we will call these "monitors".
我认为你想要实现的东西不需要太多的机器。
newtype StreamTransformer input output = StreamTransformer
{ runStreamTransformer :: input -> (output, StreamTransformer input output)
}
type Monitor input = StreamTransformer input Bool
pre :: Monitor input -> Monitor input
pre st = StreamTransformer $ \i ->
-- NB: the first output of the stream transformer vanishes.
-- Is that OK? Maybe this representation doesn't fit the spec?
let (_, st') = runStreamTransformer st i
in (False, st')
and :: Monitor input -> Monitor input -> Monitor input
and left right = StreamTransformer $ \i ->
let (bleft, mleft) = runStreamTransformer left i
(bright, mright) = runStreamTransformer right i
in (bleft && bright, mleft `and` mright)
这个StreamTransformer
不一定有状态的,但是承认有状态的。您不需要(IMO 不应该!在大多数情况下!!)为了定义这些类型类(或者甚至永远!:),但这是另一个话题)。
notStateful :: StreamTransformer input ()
notStateful = StreamTransformer $ \_ -> ((), notStateful)
stateful :: s -> (input -> s -> (output, s)) -> StreamTransformer input output
stateful s k = StreamTransformer $ \input ->
let (output, s') = k input s
in (output, stateful s' k)
alternateBool :: Monitor anything
alternateBool = stateful True $ \_ s -> (s, not s)
对于您的第一个问题,正如 Carl 提到的,lens
中的 zoom
完全符合您的要求。你的镜头代码可以这样写:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Monad.State.Lazy
newtype InnerState = MkInnerState { _innerVal :: Int }
deriving (Eq, Ord, Read, Show)
data OuterState = MkOuterState
{ _outerTrigger :: Bool
, _inner :: InnerState
} deriving (Eq, Ord, Read, Show)
makeLenses ''InnerState
makeLenses ''OuterState
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = do
i <- gets _innerVal
put $ MkInnerState (i + 1)
return i
outerStateFoo :: Monad m => StateT OuterState m Int
outerStateFoo = do
b <- gets _outerTrigger
if b
then zoom inner $ innerStateFoo
else pure 666
编辑:如果您已经引入了 lens
,那么 innerStateFoo
可以这样写:
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = innerVal <<+= 1
我正在尝试定义一系列状态机,这些状态机具有不同类型的状态。特别是,更多 "complex" 状态机具有通过组合更简单状态机的状态而形成的状态。
(这类似于面向对象的设置,其中一个对象具有多个属性,这些属性也是对象。)
这是我想要实现的目标的简化示例。
data InnerState = MkInnerState { _innerVal :: Int }
data OuterState = MkOuterState { _outerTrigger :: Bool, _inner :: InnerState }
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = do
i <- _innerVal <$> get
put $ MkInnerState (i + 1)
return i
outerStateFoo :: Monad m => StateT OuterState m Int
outerStateFoo = do
b <- _outerTrigger <$> get
if b
then
undefined
-- Here I want to "invoke" innerStateFoo
-- which should work/mutate things
-- "as expected" without
-- having to know about the outerState it
-- is wrapped in
else
return 666
更一般地说,我想要一个通用框架,其中这些嵌套更复杂。这是我想知道如何做的事情。
class LegalState s
data StateLess
data StateWithTrigger where
StateWithTrigger :: LegalState s => Bool -- if this trigger is `True`, I want to use
-> s -- this state machine
-> StateWithTrigger
data CombinedState where
CombinedState :: LegalState s => [s] -- Here is a list of state machines.
-> CombinedState -- The combinedstate state machine runs each of them
instance LegalState StateLess
instance LegalState StateWithTrigger
instance LegalState CombinedState
liftToTrigger :: Monad m, LegalState s => StateT s m o -> StateT StateWithTrigger m o
liftToCombine :: Monad m, LegalState s => [StateT s m o] -> StateT CombinedState m o
就上下文而言,这就是我想用这台机器实现的目标:
我想设计这些叫做 "Stream Transformers" 的东西,它们基本上是有状态的函数:它们消耗一个令牌,改变它们的内部状态并输出一些东西。具体来说,我对输出为布尔值的 class 流转换器感兴趣;我们将这些称为 "monitors".
现在,我正在尝试为这些对象设计组合器。其中一些是:
- 一个
pre
组合子。假设mon
是一个监视器。然后,pre mon
是一个监视器,它总是在第一个令牌被消耗后生成False
,然后模仿mon
的行为,就好像现在正在插入前一个令牌一样。我想在上面的示例中使用StateWithTrigger
对pre mon
的状态进行建模,因为新状态是一个布尔值以及原始状态。 - 一个
and
组合器。假设m1
和m2
是监视器。然后,m1 `and` m2
是一个监视器,它将令牌馈送到 m1,然后馈送到 m2,如果两个答案都为真,则生成True
。我想在上面的示例中使用CombinedState
对m1 `and` m2
的状态进行建模,因为必须维护两个监视器的状态。
For context, this is what I want to achieve with this machinery:
I want to design these things called "Stream Transformers", which are basically stateful functions: They consume a token, mutate their internal state and output something. Specifically, I am interested in a class of Stream Transformers where the output is a Boolean value; we will call these "monitors".
我认为你想要实现的东西不需要太多的机器。
newtype StreamTransformer input output = StreamTransformer
{ runStreamTransformer :: input -> (output, StreamTransformer input output)
}
type Monitor input = StreamTransformer input Bool
pre :: Monitor input -> Monitor input
pre st = StreamTransformer $ \i ->
-- NB: the first output of the stream transformer vanishes.
-- Is that OK? Maybe this representation doesn't fit the spec?
let (_, st') = runStreamTransformer st i
in (False, st')
and :: Monitor input -> Monitor input -> Monitor input
and left right = StreamTransformer $ \i ->
let (bleft, mleft) = runStreamTransformer left i
(bright, mright) = runStreamTransformer right i
in (bleft && bright, mleft `and` mright)
这个StreamTransformer
不一定有状态的,但是承认有状态的。您不需要(IMO 不应该!在大多数情况下!!)为了定义这些类型类(或者甚至永远!:),但这是另一个话题)。
notStateful :: StreamTransformer input ()
notStateful = StreamTransformer $ \_ -> ((), notStateful)
stateful :: s -> (input -> s -> (output, s)) -> StreamTransformer input output
stateful s k = StreamTransformer $ \input ->
let (output, s') = k input s
in (output, stateful s' k)
alternateBool :: Monitor anything
alternateBool = stateful True $ \_ s -> (s, not s)
对于您的第一个问题,正如 Carl 提到的,lens
中的 zoom
完全符合您的要求。你的镜头代码可以这样写:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Monad.State.Lazy
newtype InnerState = MkInnerState { _innerVal :: Int }
deriving (Eq, Ord, Read, Show)
data OuterState = MkOuterState
{ _outerTrigger :: Bool
, _inner :: InnerState
} deriving (Eq, Ord, Read, Show)
makeLenses ''InnerState
makeLenses ''OuterState
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = do
i <- gets _innerVal
put $ MkInnerState (i + 1)
return i
outerStateFoo :: Monad m => StateT OuterState m Int
outerStateFoo = do
b <- gets _outerTrigger
if b
then zoom inner $ innerStateFoo
else pure 666
编辑:如果您已经引入了 lens
,那么 innerStateFoo
可以这样写:
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = innerVal <<+= 1