状态设计模式的功能等价

Raj*_*lli 12 c# oop haskell design-patterns functional-programming

什么是功能编程相当于State设计模式?或者更具体地说,这个维基百科的状态设计模式的例子将如何转化为FP?

Don*_*art 7

这种模式是使用State monad的一个例子,一个计算环境可以用状态增加代码.

这是Haskell中的一个实现.

一些助手:

import Control.Monad.Trans.State
import Control.Monad.IO.Class
import Data.Char
Run Code Online (Sandbox Code Playgroud)

程序的两种操作模式

data Mode = A | B
Run Code Online (Sandbox Code Playgroud)

使用此模式的有状态计算类型,使用计数器进行扩充.

type StateM a = StateT (Int, Mode) IO a
Run Code Online (Sandbox Code Playgroud)

write函数是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)
Run Code Online (Sandbox Code Playgroud)

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

main = flip runStateT (0, A) $ do
    writeName "Monday"
    writeName "Tuesday"
    writeName "Wednesday"
    writeName "Thursday"
    writeName "Saturday"
    writeName "Sunday"
Run Code Online (Sandbox Code Playgroud)

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

monday
TUESDAY
WEDNESDAY
thursday
SATURDAY
SUNDAY
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为*惯用*是将具有多个变体的对象转换为代数数据类型. (3认同)

luq*_*qui 5

一种编码:

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)
Run Code Online (Sandbox Code Playgroud)

不要被愚弄IO,这是该模式的纯粹翻译(我们不使用IORef存储状态或任何东西).扩展了newtype,我们看到这种类型意味着什么:

State = String -> IO (String -> IO (String -> IO (String -> ...
Run Code Online (Sandbox Code Playgroud)

它需要一个字符串,做一些I/O并要求另一个字符串等.

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

newtype State声明采用抽象的地方writeName声明及其签名.StateContext我们只是让它返回新状态,而不是传递我们分配新状态的东西.嵌入返回值IO表示允许新状态依赖于I/O. 由于在这个例子中技术上不需要,我们可以使用更严格的类型

newtype State = State { unState :: String -> (State, IO ()) }
Run Code Online (Sandbox Code Playgroud)

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

而对于"测试客户":

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" ]
Run Code Online (Sandbox Code Playgroud)


fuz*_*fuz 1

也许使用Statemonad 与自定义修饰符和访问器相结合?