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