Монады Haskell могут считаться использованием и возвратом скрытого параметра состояния?

Я не понимаю точную алгебру и теорию позади монад Haskell. Однако, когда я думаю о функциональном программировании в целом, я получаю впечатление, что состояние было бы смоделировано путем взятия начального состояния и генерации копии его для представления следующего состояния. Это похоже, когда один список добавляется другому; никакой список не изменяется, но третий список создается и возвращается.

Это поэтому допустимо для размышления об одноместных операциях как о неявном взятии объекта начального состояния в качестве параметра и неявно возврате объекта конечного состояния? Эти объекты состояния были бы скрыты так, чтобы программист не волновался о них и управлять, как к ним получают доступ. Так, программист не попытался бы скопировать объект, представляющий поток IO, как это было десять минут назад.

Другими словами, если у нас есть этот код:

main = do
    putStrLn "Enter your name:"
    name <- getLine
    putStrLn ( "Hello " ++ name )

... это в порядке для размышления о монаде IO и "делает" синтаксис как представляющий этот стиль кода?

putStrLn :: IOState -> String -> IOState
getLine :: IOState -> (IOState, String)
main :: IOState -> IOState

-- main returns an IOState we can call "state3"
main state0 = putStrLn state2 ("Hello " ++ name)
    where (state2, name) = getLine state1
        state1 = putStrLn state0 "Enter your name:"
9
задан AJM 11 May 2010 в 22:53
поделиться

5 ответов

Нет, монады в целом это не делают. Однако ваша аналогия на самом деле совершенно верна в отношении типа данных State s a , который оказывается a монадой. Состояние определяется следующим образом:

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

... где переменная типа s - это значение состояния, а a - это «обычное» значение, которое вы используете . Таким образом, значение в «монаде состояния» - это просто функция от начального состояния до возвращаемого значения и конечного состояния. Монадический стиль в применении к State не делает ничего, кроме автоматической передачи значения состояния через последовательность функций.

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

Монада IO по сути является монадой ST , настроенной на «больше магии», с побочными эффектами, затрагивающими внешний мир, и только одной точкой, где IO ] вычисления выполняются, а именно точка входа для всей программы. Тем не менее, на некотором концептуальном уровне вы все еще можете думать об этом как о передаче значения «состояния» через функции, как это делает обычный State .

Однако другие монады не обязательно имеют какое-либо отношение к состоянию потоковой передачи, функциям упорядочивания и т. Д. Операции, необходимые для того, чтобы что-то было монадой, невероятно являются общими и абстрактными. Например, использование Может быть или Либо в качестве монад позволяет использовать функции, которые могут возвращать ошибки, с обработкой монадического стиля, выходящей из вычислений при возникновении ошибки так же, как State передает значение состояния. Использование списков в качестве монады дает вам недетерминизм , позволяя одновременно применять функции к нескольким входам и видеть все возможные выходы, при этом монадический стиль автоматически применяет функцию к каждому аргументу и собирает все выходные данные.

18
ответ дан 4 December 2019 в 06:49
поделиться

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

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

Распределение состояний через последовательность вычислений с отслеживанием состояния - это единственный пример операции, которая удовлетворяет законам монад.

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

8
ответ дан 4 December 2019 в 06:49
поделиться

Не монады в целом, но для монады IO, да - фактически, тип IO a часто определяется как тип функции RealWorld -> (RealWorld, a). Таким образом, с этой точки зрения, десуггерированный тип putStrLn - это String -> RealWorld -> (RealWorld, ()), а getChar - это RealWorld -> (RealWorld, Char) - и мы применяем его только частично, а монадическое связывание заботится о полной оценке и передаче RealWorld. (Библиотека ST в GHC на самом деле включает очень реальный тип RealWorld, хотя он описан как "глубоко магический" и не предназначен для реального использования.)

Есть много других монад, которые не имеют этого свойства, однако. Например, монады [1,2,3,4] или Просто "Hello" не передают RealWorld.

4
ответ дан 4 December 2019 в 06:49
поделиться

Я предпочитаю думать о монадах как об объектах, которые представляют отложенные действия (runXXX, main) с результатом, который можно комбинировать в соответствии с этим результатом.

return "somthing"
actionA >>= \x -> makeActionB x

И эти действия не обязательно должны быть полнофункциональными. Т.е. вы можете думать о монаде построения функций следующим образом:

instance Monad ((->) a) where
    m >>= fm = \x -> fm (m x) x
    return = const

sqr = \x -> x*x
cube = \x -> x*x*x

weird = do
    a <- sqr
    b <- cube
    return (a+b)
1
ответ дан 4 December 2019 в 06:49
поделиться

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

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

Крайний пример (просто теоретический пример, вы вряд ли захотите это делать) может быть таким: Язык C допускает нечистые вычисления, поэтому вы можете определить тип, который представляет собой часть кода на языке C. Затем вы можете написать интерпретатор, который берет одну из этих структур Си и интерпретирует ее. Все монады подобны этому, хотя обычно намного проще, чем интерпретатор языка Си. Но это представление более мощное, потому что оно также объясняет монады, которые не связаны с передачей скрытого состояния.

Вероятно, вам также следует попытаться противостоять искушению рассматривать IO как передачу скрытого состояния мира. Внутренняя реализация IO не имеет никакого отношения к тому, как вы должны думать об IO. Монада IO - это построение представлений ввода-вывода, которые вы хотите выполнить. Вы возвращаете одно из этих представлений системе времени выполнения Haskell, и эта система может реализовать ваше представление так, как ей захочется.

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

3
ответ дан 4 December 2019 в 06:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: