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

Путаница над кодом государственной Монады на тему "Учите вас в Haskell"

Я пытаюсь понять Haskell, используя онлайн-книгу Учите вас Haskell для отличного хорошего.

Я, насколько мне известно, смог понять Монады до тех пор, пока не попаду в главу, в которой представлена ​​State Monad.

Однако код, представленный и заявленный как реализация типа Monad типа State (я не смог найти его в Hoogle), кажется слишком большим для меня.

  • Начнем с того, что я не понимаю логику, лежащую в ее основе, то есть, почему она должна работать и как автор рассматривал эту технику (могут быть предложены соответствующие статьи или справочные документы?)

  • В строке 4 предполагается, что функция f принимает 1 параметр.
    Однако несколько строк вниз мы представляем поп, который не принимает параметров!

  • Чтобы расширить точку 1, то, что пытается сделать автор, используя функцию для представления состояния.

Любая помощь в понимании того, что происходит, очень ценится.

Изменить

К кому это может относиться,

Ниже приведены ответы на мои вопросы. Одна вещь, которую я хотел бы добавить:

После прочтения статьи, предложенной ниже, я нашел ответ на мой второй пункт выше: Все это время я предполагал, что функция pop будет использоваться как:
 stuff >>= pop, так как в типе связывания второй параметр является функцией, тогда как правильное использование - это pop >>= stuff, которое я понял после прочтения снова, как do-notation транслируется на простой bind-lambdas.

4b9b3361

Ответ 1

Короткий ответ:

  • State предназначен для использования функций монадов, чтобы имитировать императивно-подобное системное состояние с локальными переменными. Основная идея состоит в том, чтобы скрыть внутри монады активность взятия в текущем состоянии и возвращения нового состояния вместе с промежуточным результатом на каждом шаге (и здесь мы имеем s -> (a,s).
  • Не допускайте ошибки с помощью функций State. Первый может иметь любой тип, который вам нужен (при условии, что в конечном итоге они произведут некоторый State a, если вы хотите использовать их в государственной монаде). Последняя содержит функции типа s -> (a,s): это уровень передачи состояния, управляемый монадой.
  • Как я уже сказал, функция, заключенная в State, фактически создается с помощью (>>=) и return, поскольку они определены для экземпляра Monad (State s). Его роль состоит в том, чтобы передать состояние через вызовы вашего кода.

Точка 3 также является причиной того, что параметр состояния исчезает из функций, фактически используемых в государственной монаде.

Длинный ответ:

Государственная Монада изучается в разных документах и ​​существует также в рамках Haskell (я не помню хороших ссылок прямо сейчас, я добавлю их как можно скорее).

Это та идея, что это следует: рассмотрим тип data MyState = ..., значения которого содержат текущее состояние системы.

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

Функция, завернутая в State, не принимает параметры, кроме текущего состояния, которое необходимо для создания в результате нового состояния и промежуточного результата. Функции с большим количеством параметров, которые вы видели в примерах, не являются проблемой, потому что, когда вы используете их в do -notation внутри монады, вы примените их ко всем "дополнительным" необходимым параметрам, что означает, что каждый из они приведут к частично примененной функции, единственным оставшимся параметром которой является состояние; экземпляр monad для State сделает все остальное.

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

Вычисление будет выполнено путем передачи всему блоку/композиции первого/начального состояния системы.

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

Ответ 2

Монада State представляет расчеты с состоянием, то есть вычисления, которые используют значения и, возможно, модифицируют какое-либо внешнее состояние. Когда вы последовательно выполняете вычисления с учетом состояния, последующие вычисления могут давать разные результаты в зависимости от того, как предыдущие вычисления изменяли состояние.

Так как функции в Haskell должны быть чистыми (т.е. не иметь побочных эффектов), мы имитируем эффект внешнего состояния, требуя, чтобы каждая функция принимала дополнительный параметр, представляющий текущее состояние мира, и возвращает дополнительное значение, представляющее модифицированное государство. Фактически, внешнее состояние пронизывается через последовательность вычислений, как в этой мерзости диаграммы, которую я просто рисовал в MSPaint:

enter image description here

Обратите внимание, что каждый блок (представляющий вычисление) имеет один вход и два выхода.

Если вы посмотрите на экземпляр Monad для State, вы увидите, что определение (>>=) указывает вам, как это сделать. В нем говорится, что для привязки вычисления состояния c0 к функции f, которая принимает результаты вычисления с учетом состояния и возвращает другое вычисление с учетом состояния, мы делаем следующее:

  • Запустите c0, используя начальное состояние s0, чтобы получить результат и новое состояние: (val, s1)
  • Подайте val функции f, чтобы получить новое вычисление с учетом состояния, c1
  • Запустите новое вычисление c1 с измененным состоянием s1

Как это работает с функциями, которые уже принимают аргументы n? Поскольку каждая функция в Haskell имеет значение по умолчанию, мы просто добавляем дополнительный аргумент (для состояния) в конец, а вместо нормального возвращаемого значения функция теперь возвращает пару, второй элемент которой является новым измененным состоянием. Поэтому вместо

f :: a -> b

теперь имеем

f :: a -> s -> (b, s)

Вы можете подумать, что

f :: a -> ( s -> (b, s) )

что является одним и тем же в Haskell (так как состав функций является правильным ассоциативным), который читает "f - это функция, которая принимает аргумент типа a и возвращает вычисление с учетом состояния". И это действительно все, что есть в монаде State.

Ответ 3

Монада State по существу

type State s a = s -> (a,s)

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

Таким образом, функция, которая принимает аргументы k > 0, одна из которых является состоянием и возвращает пару чего-то и новое состояние, в монаде State s становится функцией, принимающей аргументы k-1 и возвращающей монадическое действие ( который в основном представляет собой функцию, принимающую один аргумент, состояние здесь).

В не-состоянии параметр pop принимает один аргумент, стек, который является состоянием. Таким образом, в монадической настройке pop становится действием State Stack Int без явного аргумента.

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

Ответ 4

Я полный новичок в Haskell, и я не мог хорошо понять код государственной монады в этой книге. Но позвольте мне добавить мой ответ здесь, чтобы помочь кому-то в будущем.

Ответы:

  • Что они пытаются выполнить с помощью State Monad?

    Составляющие функции, которые обрабатывают вычисления с учетом состояния.
    например push 3 >>= \_ -> push 5 >>= \_ -> pop

  • Почему pop не принимает никаких параметров, в то время как предлагается функция f принимает 1 параметр?

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

    runState pop :: Stack -> (Int, Stack)
    runState (push 3) :: Stack -> ((), Stack)
    

    если вы имеете в виду правую часть >>= с помощью функции f ", f будет выглядеть как \a -> pop или \a -> push 3, а не только pop.


Длинное пояснение:

Эти 3 вещи помогли мне лучше понять пример штата Монада и Stack.

  • Рассмотрим типы аргументов для оператора bind (>>=)

    Определение оператора bind в стандартном классе Monad - это

    (>>=) :: (Monad m) => m a -> (a -> m b) -> m b
    

    В примере Stack m есть State Stack.
    Если мы ментально заменим m на State Stack, определение может быть таким.

    (>>=) :: State Stack a -> (a -> State Stack b) -> State Stack b 

    Следовательно, тип аргумента левой стороны для оператора привязки будет State Stack a.
    А правая сторона будет a -> State Stack b.

  • Перевести обозначение для привязки оператора

    Вот пример кода, использующего обозначения в книге.

    stackManip :: State Stack Int  
    stackManip = do  
         push 3  
         pop  
         pop  
    

    он может быть переведен на следующий код с оператором bind.

    stackManip :: State Stack Int  
    stackManip = push 3 >>= \_ -> pop >>= \_ -> pop
    

    Теперь мы можем видеть, какая будет правая сторона для оператора bind.
    Их типы a -> State Stack b.

    (\_ -> pop) :: a -> State Stack Int
    (\_ -> push 3) :: a -> State Stack ()
    


  • Признать разницу между (State s) и (State h) в объявлении экземпляра

    Вот объявление экземпляра для состояния в книге.

    instance Monad (State s) where  
        return x = State $ \s -> (x,s)  
        (State h) >>= f = State $ \s -> let (a, newState) = h s  
                                            (State g) = f a  
                                        in  g newState 
    

    Учитывая типы с примером Stack, тип (State s) будет

    (State s) :: State Stack
    s :: Stack
    

    И тип (State h) будет

    (State h) :: State Stack a
    h :: Stack -> (a, Stack)
    

    (State h) - это аргумент левой стороны оператора связывания, а его тип State Stack a, как описано выше.

    Тогда почему h становится Stack -> (a, Stack)?
    Это результат сопоставления шаблонов с конструктором значений состояния, который определен в оболочке newtype. То же самое относится к (State g).

    newtype State s a = State { runState :: s -> (a,s) }
    

    В общем случае тип h - это s ->(a, s), представление вычисления состояния. Любое из следующих действий может быть h в примере Stack.

    runState pop :: Stack -> (Int, Stack)
    runState (push 3) :: Stack -> ((), Stack)
    runState stackManip :: Stack -> (Int, Stack)
    

    что он.