Как на самом деле «получить» / получить / исходное состояние в Haskell?
-
10-07-2019 - |
Вопрос
У меня есть функция:
test :: String -> State String String
test x =
get >>= \test ->
let test' = x ++ test in
put test' >>
get >>= \test2 -> put (test2 ++ x) >>
return "test"
Я вполне понимаю, что происходит в этой функции, и начинаю разбираться в монадах.Чего я не понимаю, так это как, когда я запускаю это:
runState (test "testy") "testtest"
функция «get» в «test» каким-то образом получает начальное состояние «testtest».Может ли кто-нибудь разобрать это и объяснить мне?
Я ценю любые ответы!
Решение
Первоначально я собирался опубликовать это как комментарий, но решил рассказать немного подробнее.
Строго говоря, get
не " берет " Аргумент. Я думаю, что многое из того, что происходит, маскируется тем, что вы не видите - определениями экземпляров Государственной монады.
get
на самом деле является методом класса MonadState. Монада State является экземпляром MonadState, обеспечивающим следующее определение get
:
get = State $ \s -> (s,s)
Другими словами, get
просто возвращает очень простую монаду State (помня, что монаду можно рассматривать как «оболочку» для вычисления), где любой входной s
в вычислении вернет пару s
в качестве результата.
Следующее, что нам нужно рассмотреть, это > > =
, которое State определяет следующим образом:
m >>= k = State $ \s -> let
(a, s') = runState m s
in runState (k a) s'
Итак, > > =
приведет к новому вычислению, которое не будет вычислено, пока не получит начальное состояние (это верно для всех вычислений состояний, когда они находятся в их «завернутая» форма). Результат этого нового вычисления достигается путем применения того, что находится справа от > > =
, к результату выполнения вычисления, которое было слева. (Это довольно запутанное предложение, которое может потребовать дополнительного чтения или двух.)
Я нашел это весьма полезным для "desugar" все, что происходит Это займет намного больше времени, но ответ на ваш вопрос (откуда берется get
) должен быть очень ясным. Обратите внимание, что следующее должно рассматриваться как psuedocode ...
test x =
State $ \s -> let
(a,s') = runState (State (\s -> (s,s))) s --substituting above defn. of 'get'
in runState (rightSide a) s'
where
rightSide test =
let test' = x ++ test in
State $ \s2 -> let
(a2, s2') = runState (State $ \_ -> ((), test')) s2 -- defn. of 'put'
in runState (rightSide2 a2) s2'
rightSide2 _ =
-- etc...
Это должно сделать очевидным, что конечный результат нашей функции - это новое вычисление State, которому потребуется начальное значение ( s
), чтобы остальная часть вещи произошла. Вы указали s
как " testtest "
с вашим вызовом runState
. Если вы замените " testtest " для s
в приведенном выше псевдокоде вы увидите, что в первую очередь мы запускаем get
с " testtest " как «начальное состояние». В результате получается (" testtest " ;, " testtest ")
и т. Д.
Итак, вот где get
получает ваше начальное состояние " testtest " ;. Надеюсь, это поможет!
Другие советы
Возможно, это поможет вам глубже взглянуть на то, что State
конструктор типа на самом деле есть, и как runState его использует.В GHCi:
Prelude Control.Monad.State> :i State
newtype State s a = State {runState :: s -> (a, s)}
Prelude Control.Monad.State> :t runState
runState :: State s a -> s -> (a, s)
State
принимает два аргумента:тип состояния и возвращаемый тип.Он реализован как функция, принимающая начальное состояние и возвращающая возвращаемое значение и новое состояние.
runState
принимает такую функцию в качестве исходных входных данных и (скорее всего) просто применяет одну функцию к другой, чтобы получить пару (результат, состояние).
Твой test
функция представляет собой большую композицию State
Функции типа -, каждая из которых принимает входные данные о состоянии и выдает выходные данные (результат, состояние), подключаются друг к другу таким образом, чтобы это имело смысл для вашей программы.Все runState
делает, это предоставляет им отправную точку состояния.
В данном контексте, get
— это просто функция, которая принимает состояние в качестве входных данных и возвращает выходные данные (результат, состояние), так что результат является входным состоянием, а состояние остается неизменным (выходное состояние является входным состоянием).Другими словами, get s = (s, s)
Переход к главе 8 («Функциональные парсеры») из Грэма Хаттона. Программирование на Haskell несколько раз, пока я правильно не понял это, а затем пошло по учебнику All About Monads , сделал это для меня.
Проблема с монадами заключается в том, что они очень полезны для нескольких вещей, которые те из нас, которые приходят из обычного программирования, находят весьма непохожими. Требуется некоторое время, чтобы понять, что поток управления и состояние обработки не только достаточно похожи, что они могут обрабатываться одним и тем же механизмом, но и когда вы отступаете достаточно далеко, то же самое.
Богоявление пришло, когда я рассматривал структуры управления в C ( for
и while
и т. д.), и я понял, что наиболее распространенной структурой управления на сегодняшний день является просто ставить одно утверждение перед другим. Прошел год изучения Хаскелла, прежде чем я понял, что это даже контрольная структура.