Функциональный эквивалент шаблона государственного проектирования

Каким будет функциональный программный эквивалент шаблона государственного дизайна? Или более конкретно, как этот пример Википедии шаблона государственного дизайна будет переведен в FP?

Ответ 1

Этот шаблон является примером использования 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

Заметим, что это чисто функциональное решение. В этой программе нет изменчивых или деструктивных обновлений. Вместо этого, государственная монада переводит нужный режим через вычисление.

Ответ 2

Одна кодировка:

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 -> ...

Он принимает строку, выполняет некоторые операции ввода-вывода и запрашивает другую строку и т.д.

Это моя любимая кодировка абстрактных шаблонов классов в OO: abstract class → type, subclasses → элементы этого типа.

Объявление newtype State заменяет декларацию writeName и ее подпись. Вместо передачи StateContext, в которое мы назначаем новое состояние, мы просто возвращаем новое состояние. Вложение возвращаемого значения в IO говорит о том, что новое состояние может зависеть от ввода-вывода. Поскольку это не является технически необходимым в этом примере, мы могли бы использовать более строгий тип

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" ]

Ответ 3

Может быть, с монадой State в сочетании с настраиваемыми модификаторами и аксессуарами?

Ответ 4

Я не думаю, что существует чистый функциональный эквивалент для шаблона состояния. Потому что чисто функциональное программирование не имеет понятия о состоянии и времени. Структура состояния - это внутреннее состояние и время. Но я думаю, что существует нечистый функциональный эквивалент, бесконечный ленивый оцениваемый поток. Вы можете реализовать его с выходом С#.