Как «получить» на самом деле / ​​получить / начальное состояние в Haskell? - PullRequest
12 голосов
/ 24 июня 2009

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

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

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

Ответы [ 3 ]

18 голосов
/ 24 июня 2009

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

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

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

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

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

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

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

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

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

5 голосов
/ 24 июня 2009

Это может помочь вам глубже взглянуть на то, чем на самом деле является конструктор типа 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)

1 голос
/ 24 июня 2009

Пройдя 8-ю главу («Функциональные парсеры») о программировании Грэма Хаттона на Хаскелле , несколько раз, пока я его правильно не понял, после чего изучите учебник Все о монадах , сделал этот клик для меня.

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

Богоявление пришло, когда я рассматривал управляющие структуры в C (for и while и т. Д.), И я понял, что на сегодняшний день наиболее распространенной управляющей структурой было просто поставить одно утверждение перед другим. Прошел год изучения Хаскелла, прежде чем я понял, что это даже контрольная структура.

...