2011-06-11 3 views
11

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

ответ

1

Возможно с помощью монады State в сочетании с пользовательскими модификаторами и аксессуарами?

+0

Согласен, выглядит как конкретное использование 'State' монады для меня тоже. –

-3

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

+1

FP действительно имеет состояние. Никогда не слышал о монадах? Существует даже что-то, называемое функциональным реактивным программированием для захвата изменяющегося во времени состояния. – fuz

+0

Обратите внимание, что я использовал слова «чистый» и «не чистый» – Dagang

+1

Я не думаю, что «имеет» - это правильное слово для подключения «функционального программирования» и «состояния». Мне нравится думать об этом как о «моделях» - мы можем делать расчеты с учетом состояния, создавая функциональную модель того, что означает государство. – luqui

7

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

Из приведенного выше кода выход основного тока:

monday 
TUESDAY 
WEDNESDAY 
thursday 
SATURDAY 
SUNDAY 

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

+1

Я бы не сказал, что это верная кодировка: здесь мы должны объявить все режимы в одном месте ('Data Mode'), тогда как в примерах википедии примеры могут быть объединены модульно. – luqui

+0

Это стандартный перевод на закрытые типы данных в языках ML-стиля. Очевидно, что мы могли бы отказаться от использования открытых типов данных с помощью функций или классов типов, но я думаю, что это не относится к этой проблеме. –

+1

Мне любопытно, что это стандартный перевод, и что вы подразумеваете под стандартом. – luqui

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) 

Не обманывайте на IO, это чистый перевод этого шаблона (мы не используем в IORef для хранения состояния или что-нибудь). Расширение newtype, мы видим, что означает, что этот тип:

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

Он принимает строку, делает некоторые I/O и запрашивает другую строку и т.д.

Это мое любимое кодирование абстрактных моделей класса в 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" ] 
+0

P.S. Вам нужно будет также захватить смену семантики. –

+0

@Don, о, справа. hmm .... – luqui

+0

Для чего это стоит, я бы сказал, что этот стиль кодирования идеален только для истинных OO-идиоматических классов, инкапсулирующих абстрактное поведение. Удручающее количество «классов» в якобы OO-коде гораздо ближе к неуклюжим переводам стиля, которые использовал Дон, и, таким образом, выгоду от того, чтобы вернуться к тому, кем они хотели быть в первую очередь ... –

Смежные вопросы