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

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

Ответы [ 4 ]

7 голосов
/ 11 июня 2011

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

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

5 голосов
/ 11 июня 2011

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

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

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

Это мое любимое кодирование шаблонов абстрактных классов в ОО: абстрактный класс -> тип, подклассы -> элементы этого типа.

Декларация 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" ]
1 голос
/ 11 июня 2011

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

0 голосов
/ 11 июня 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...