2009-06-24 5 views
12

У меня есть функция:Как «получить» на самом деле/​​получить/начальное состояние в 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" 

функцию «получить» в «тесте» каким-то образом получает начальное состояние «testtest». Может кто-то сломает это и объяснит мне?

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

ответ

18

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

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

get на самом деле является методом класса MonadState. Государство монада является экземпляром 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' 

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

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

Я не мог придумать лучшего слова, когда сказал, что «принял» аргумент. Спасибо за это очень подробное объяснение. – Rayne

5

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

Clickedy click click! Бинго. Большое спасибо, вы объяснили, что щелкнули. Я никогда не понимал, что на самом деле «взял» аргумент. Хехе. Благодарю. – Rayne

1

Просматривая главу 8 («Функциональные парсеры») от Graham Hutton's Programming in Haskell несколько раз, пока я не понял ее правильно, а затем зайдите в учебник All About Monads, сделал этот щелчок для меня.

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

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

+0

Спасибо за ответ. К счастью, большая часть моего опыта программирования на самом деле заключается в функциональном программировании, поэтому мне не так сложно! : D – Rayne