2014-12-03 3 views
0

У меня есть этот фрагмент, не могли бы вы объяснить мне, что это делает, особенно >> =?Haskell State monad explain

instance Monad (State s) where 
    return a = State $ \s -> (a, s) 
    State st >>= f = State $ \s -> 
     let (a, s') = st s 
     in runState (f a) s' 

В конце концов вы могли бы написать его в более четкой форме? Thanks

+1

'>> =' является сердцем * монады *, поэтому ваш вопрос вроде сводится к * «что такое монады?» *. Поскольку объем этого вопроса немного велик, я предлагаю вам ознакомиться с введением, таким как [глава монады LYAH] (http://learnyouahaskell.com/a-fistful-of-monads). – gspr

+0

@gspr Однако речь идет о конкретной монаде. Мне кажется, что спрашивать, как работает 'concatMap', спрашивает, как работают монады, потому что' concatMap = flip (>> =) '. –

ответ

6

Этот код определяет что должно быть >>=.

State func1 >>= f = 
    State $ ...stuff... 

State Конструктор должен следовать функции, которая принимает текущее состояние и возвращает новое состояние и результат. Таким образом, ...stuff... должна быть такой функцией, и func1 также должен быть таким.

State $ \ state1 -> ... 

ОК, так что мы хотим «запустить» func1, дав ему какое-то состояние:

let (resultA, state2) = func1 state1 

Далее мы хотим вызвать f, чтобы получить следующий монадическое действие сделать:

State func2 = f resultA 

и теперь мы "запустить", что, подавая его state2, а не state1:

in func2 state2 

В приведенном выше примере кода используется runState, но я думал, что я четко сформулировал его для ясности.

State func1 >>= f = 
    State $ state1 -> 
    let (resultA, state2) = func1 state1 
     State func2 = f resultA 
    in func2 state2 
4

Представьте динамическую функцию:

type StatefulFunction s a b = s -> a -> (b, s) 

Давайте создадим две такие функции:

a :: StatefulFunction Int Int Int 
a s x = (x + s, s) 

b :: StatefulFunction Int Int Int 
b s x = (x + s, s+1) 

Эти функции могут изменить поведение на основе явного параметра состояния. Тем не менее, сцепление их утомительно:

let startState = 0 

let aParam = 42 
let (aResult, newState) = a startState x 

let bParam = 99 
let (bResult, newState') = b newState y 

State монада делает это сцепление легче, и то, что я написал выше, что именно >>= делает для него:

a :: Int -> State Int Int 
a x = fmap (+x) get 

b :: Int -> State Int Int 
b x = do 
    s <- get 
    put $ s + 1 
    return $ s + x 

foo :: State Int Int 
foo = (a aParam) >>= (b bParam) 
5

Если вы косоглазие, вы можете увидеть, что State действительно является оберткой для функциональности, относящейся к функциям типа (a,s)->(b,s), только в карри: a -> s -> (b,s).

Цепные функции типа (a,s)->(b,s) действительно просты - простая композиция. Таким образом, они могут пройти как «состояние» (s), так и вычислительные «результаты» (a для создания b) по цепочке вызовов. Конечно, становится очевидным, что различие между «состоянием» и «результатами» произвольно. Но если вы имеете дело с карнидной версией, это становится немного более сложным, и об этом заботится >>=.

Но что такое Monad, если мы можем «цеплять очень легко» функции типа до каррирования?Monad должен заботиться о шаблоном, связанном с распространением состояния, даже если функция не зависит от него, потому что большинство функций этого не делают. Они начинаются в зависимости от состояния и изменения состояния (ну, на самом деле, они становятся полностью работоспособными), когда они получают доступ ко второй проекции кортежа, используя get и put. Без State монады, которая будет выглядеть так:

get :: (a,s) -> (s,s) 
get (_,s) = (s,s) 
put :: (s,s) -> ((),s) 
put (s,_) = ((),s) 

По выделки (a,s)->(b,s), мы получаем гигиенический с учетом состояния вычисления, отделяя проблемы для зависимости от a и s: мы избавляемся от явной зависимости от s, если специально не обнаруживаемых с помощью использование get и put. Это важно иметь в виду их существование - в противном случае это не совсем очевидно, что возня с прохождением s вокруг:

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

Теперь вернемся к >>=. Что эта функция делает, объединяет s->(a,s) и a->s->(b,s), чтобы получить s->(b,s) - который теперь может использоваться для объединения с >>= еще. Если бы мы не имели State конструктора обертки, связывание будет выглядеть следующим образом:

as >>= f = \s -> (uncurry f) $ as s 

То есть, учитывая состояние, корм в as производить (a,s), которые подают в f (uncurried, чтобы получить (a,s)->(b,s) снова) в производят (b,s). Но так как мы имеем State оболочку, она становится немного менее очевидно:

(State as) >>= f = State $ \s -> let (a,s') = as s --mean to call f (a,s'), but 
            (State g) = f a --need to remove the wrapper 
           in g s'    --to pass s' to (s->(b,s)) 

разворачивания в вашем примере это делается с помощью runState.

Смежные вопросы