状态设计模式的功能等价

本文关键字:功能 设计模式 状态 | 更新日期: 2023-09-27 17:58:51

State设计模式的功能编程等价物是什么?或者更具体地说,这个维基百科国家设计模式的例子将如何转化为FP?

状态设计模式的功能等价

此模式是State monad(计算使用状态扩充代码的环境。

下面是Haskell中的一个实现。

一些助手:

import Control.Monad.Trans.State
import Control.Monad.IO.Class
import Data.Char

程序的两种操作模式

data Mode = A | B

使用此模式的有状态计算类型,并添加计数器。

type StateM a = StateT (Int, Mode) IO a

写函数是StateM上下文中的函数,基于状态模式更改其行为:

writeName :: String -> StateM ()
writeName s = do
    (n,mode) <- get
    case mode of
        A -> do liftIO (putStrLn (map toLower s))
                put (0,B)
        B -> do let n' = n + 1
                liftIO (putStrLn (map toUpper s))
                if n' > 1 then put (n', A)
                          else put (n', B)

运行程序,最初在状态a 中启动有状态计算

main = flip runStateT (0, A) $ do
    writeName "Monday"
    writeName "Tuesday"
    writeName "Wednesday"
    writeName "Thursday"
    writeName "Saturday"
    writeName "Sunday"

从上面的代码来看,main的输出是:

monday
TUESDAY
WEDNESDAY
thursday
SATURDAY
SUNDAY

请注意,这是一个纯粹的功能性解决方案。此程序中没有可变或破坏性的更新。相反,状态monad通过计算来执行所需的模式。

一种编码:

import Data.Char (toUpper, toLower)
newtype State = State { unState :: String -> IO State }
stateA :: State
stateA = State $ 'name -> do
    putStrLn (map toLower name)
    return stateB
stateB :: State
stateB = go 2
    where
    go 0 = stateA
    go n = State $ 'name -> do
               putStrLn (map toUpper name)
               return $ go (n-1)

不要被IO所欺骗,这是该模式的纯翻译(我们没有使用IORef来存储状态或任何东西)。扩展newtype,我们可以看到这种类型的含义:

State = String -> IO (String -> IO (String -> IO (String -> ...

它获取一个字符串,进行一些I/O并请求另一个字符串等。

这是我在OO中最喜欢的抽象类模式编码:抽象类->类型,该类型的子类->元素。

newtype State声明取代了抽象writeName声明及其签名。我们没有传递一个StateContext给它分配一个新状态,而是让它返回新状态。在IO中嵌入返回值表示允许新状态取决于I/O。由于在这个例子中,这在技术上是不必要的,我们可以使用更严格的类型

newtype State = State { unState :: String -> (State, IO ()) }

其中我们仍然可以表达这种计算,但状态序列是固定的,不允许依赖于输入。但让我们坚持原来的,更宽容的类型。

对于"测试客户端":

runState :: State -> [String] -> IO ()
runState s [] = return ()
runState s (x:xs) = do
    s' <- unState s x
    runState s' xs
testClientState :: IO ()
testClientState = runState stateA
                   [ "Monday"
                   , "Tuesday"
                   , "Wednesday"
                   , "Thursday"
                   , "Saturday"
                   , "Sunday" ]

也许使用State monad与自定义修饰符和访问器组合?

我认为状态模式没有纯粹的功能等价物。因为纯函数式编程没有状态和时间的概念。状态模式本质上是关于状态和时间的。但我认为非纯函数等价物是存在的,它是无限的惰性评估流。您可以使用C#yield来实现它。