Подтвердить что ты не робот

Как "получить" фактически/получить/начальное состояние в Haskell?

У меня есть функция:

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" в "тесте" каким-то образом получает начальное состояние "testtest". Может кто-то сломать это и объяснить мне?

Я ценю любые ответы!

4b9b3361

Ответ 1

Я изначально собирался опубликовать это как комментарий, но решил изложить немного больше.

Строго говоря, get не "принимает" аргумент. Я думаю, что многое из того, что происходит, замаскировано тем, что вы не видите, - определения экземпляра государственной монады.

get - фактически метод класса MonadState. Государственная монада является экземпляром MonadState, предоставляя следующее определение get:

get = State $ \s -> (s,s)

Другими словами, get просто возвращает очень основную монаду штата (помня, что монаду можно рассматривать как "обертку" для вычисления), где любой ввод s в вычисление вернет пару s.

Следующее, что нам нужно рассмотреть, - это >>=, которое определяет это так:

m >>= k  = State $ \s -> let
    (a, s') = runState m s
    in runState (k a) s'

Итак, >>= собирается дать новое вычисление, которое не будет вычисляться до тех пор, пока оно не получит начальное состояние (это справедливо для всех вычислений State, когда они находятся в их "завернутой" форме). Результат этого нового вычисления достигается, применяя все, что находится в правой части >>=, к результату выполнения вычисления, которое было с левой стороны. (Это довольно запутанное предложение, которое может потребовать дополнительного чтения или два.)

Я нашел весьма полезным для "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...

Это должно сделать очевидным, что конечным результатом нашей функции является вычисление нового состояния, для которого потребуется начальное значение (s), чтобы сделать остальную часть материала. Вы отправили s в качестве "testtest" своим вызовом runState. Если вы замените "testtest" на s в вышеуказанном псевдокоде, вы увидите, что первое, что происходит, - это запустить get с "testtest" как "начальное состояние". Это дает ("testtest", "testtest") и т.д.

Итак, где get получает ваше начальное состояние "testtest" . Надеюсь, это поможет!

Ответ 2

Это может помочь вам глубже понять, что такое конструктор типа 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 -type, каждый из которых принимает вход состояния и дает результат (результат, состояние), подключенный друг к другу таким образом, который имеет смысл для вашей программы. Все runState действительно предоставляют им начальную точку состояния.

В этом контексте get представляет собой просто функцию, которая принимает состояние как входной сигнал и возвращает выход (результат, состояние) таким образом, что результатом является входное состояние, и состояние не изменяется (состояние вывода является входное состояние). Другими словами, get s = (s, s)

Ответ 3

Просматривая главу 8 ( "Функциональные парсеры" ) Грэма Хаттона Программирование в Haskell несколько раз, пока я не понял ее правильно пойдите в учебнике All About Monads, сделал этот щелчок для меня.

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

Произошло прозрение, когда я рассматривал структуры управления в C (for и while и т.д.), и я понял, что наиболее распространенная структура управления просто ставит одно утверждение перед другим. Потребовался год изучения Haskell, прежде чем я понял, что это даже была структура управления.