Некоторые вопросы о монадах в Haskell

Возможно, запустите с маленьких проектов микроконтроллера. Это может быть полезно: http://www.kmitl.ac.th/~kswichit%20/

11
задан StackedCrooked 1 January 2015 в 16:13
поделиться

6 ответов

Возможность удалить букву «M» из «M t» возможна только внутри такого оператора связывания. Есть ли у этого оператора связывания какие-то особые привилегии или что-то в этом роде?

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

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

Особым случаем является монада IO , поскольку она не определена модулем, но встроенным в систему / компилятор времени выполнения. Здесь компилятор знает о внутренних деталях своей реализации и предоставляет такие функции, как IO >> = . Реализации этих функций действительно имеют особые привилегии, поскольку они живут «вне программы», но это особый случай, и этот факт не должен наблюдаться изнутри Haskell.

Какое отношение это имеет к изменениям состояния? Я понимаю (думаю), что цель монад - «обернуть» побочные эффекты, чтобы они были изолированы от остальной части программы. Но какова роль в этом оператора связывания?

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

Обычно монада (в частности, функция связывания) определяет способ, которым определенные функции должны быть объединены в более крупные функции. Этот метод объединения функций абстрагируется в монаде. Как именно работает это комбинирование или почему вы хотите комбинировать функции таким образом, не имеет значения, монада просто определяет способ комбинирования определенных функций определенным образом. (См. Также ответ «Монады для программистов на C #» , где я в основном повторяю это несколько раз с примерами.

10
ответ дан 3 December 2019 в 02:02
поделиться

Я ОЧЕНЬ рекомендую вам прочитать ( http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html ). Это дает четкую, здравомыслящую причину существования монад.

2
ответ дан 3 December 2019 в 02:02
поделиться

- это возможность удалить «M» из «M t», что возможно только внутри такого оператора связывания.

Что ж, это, безусловно, возможно внутри оператора связывания, поскольку его тип определяет:

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

Функция 'run' для вашей монады обычно тоже может делать это (чтобы вернуть чистое значение из ваших вычислений).

цель монад - «обернуть» побочные эффекты так, чтобы они были изолированы от остальной части программы

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

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

Это очень характерно для монады ввода-вывода. впрочем, не монады вообще.

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

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

Это очень характерно для монады ввода-вывода. впрочем, не монады вообще.

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

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

Это очень характерно для монады ввода-вывода. впрочем, не монады вообще.

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

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

Это очень характерно для монады ввода-вывода. впрочем, не монады вообще.

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

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

Это очень характерно для монады ввода-вывода. впрочем, не монады вообще.

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

Это очень характерно для монады ввода-вывода. впрочем, не монады вообще.

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

Это очень характерно для монады ввода-вывода. впрочем, не монады вообще.

12
ответ дан 3 December 2019 в 02:02
поделиться

The types do line up, funnily enough. Here's how.

Remember that a monad is also a functor. The following function is defined for all functors:

fmap :: (Functor f) => (a -> b) -> f a -> f b

Now the question: Do these types really line up? Well, yes. Given a function from a to b, then if we have an environment f in which a is available, we have an environment f in which b is available.

By analogy to syllogism:

(Functor Socrates) => (Man -> Mortal) -> Socrates Man -> Socrates Mortal

Now, as you know, a monad is a functor equipped with bind and return:

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

You may not know that equivalently, it's a functor equipped with return and join:

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

See how we're peeling off an m. With a monad m, you can't always get from m a to a, but you can always get from m (m a) to m a.

Now look at the first argument to (=<<). It's a function of type (a -> m b). What happens when you pass that function to fmap? You get m a -> m (m b). So, "mapping" over an m a with a function a -> m b gives you m (m b). Notice that this is exactly like the type of the argument to join. This is not a coincidence. A reasonable implementation of "bind" looks like this:

(>>=) :: m a -> (a -> m b) -> m b
x >>= f = join (fmap f x)

In fact, bind and join can be defined in terms of each other:

join = (>>= id)
4
ответ дан 3 December 2019 в 02:02
поделиться

The following is the definition of the type-class Monad.

class  Monad m  where

    (>>=)       :: forall a b. m a -> (a -> m b) -> m b
    (>>)        :: forall a b. m a -> m b -> m b
    return      :: a -> m a
    fail        :: String -> m a

    m >> k      = m >>= \_ -> k
    fail s      = error s

Each type-instance of type-class Monad defines its own >>= function. Here is an example from the type-instance Maybe:

instance  Monad Maybe  where

    (Just x) >>= k      = k x
    Nothing  >>= _      = Nothing

    (Just _) >>  k      = k
    Nothing  >>  _      = Nothing

    return              = Just
    fail _              = Nothing

As we can see, because the Maybe version of >>= is specially defined to understand the Maybe type-instance, and because it is defined in a place that has legal access to the data Maybe a data constructors Nothing and Just a, the Maybe version of >>= is able to unwrap the a's in Maybe a and pass them through.

To work through an example, we might take:

x :: Maybe Integer
x = do a <- Just 5
       b <- Just (a + 1)
       return b

De-sugared, the do-notation becomes:

x :: Maybe Integer
x = Just 5        >>= \a ->
    Just (a + 1)  >>= \b ->
    Just b

Which evaluates as:

  =                  (\a ->
    Just (a + 1)  >>= \b ->
    Just b) 5

  = Just (5 + 1)  >>= \b ->
    Just b

  =                  (\b ->
    Just b) (5 + 1)

  = Just (5 + 1)

  = Just 6
5
ответ дан 3 December 2019 в 02:02
поделиться

Я понимаю (думаю), что цель монад - «обернуть» побочные эффекты так, чтобы они были изолированы от остальной части программы.

На самом деле это немного более тонко. чем это. Монады позволяют нам моделировать последовательность в очень общем виде. Часто, когда вы разговариваете с экспертом в предметной области, вы обнаруживаете, что он говорит что-то вроде «сначала мы пробуем X. Затем мы пробуем Y, а если это не сработает, мы пробуем Z». Когда вы приходите к реализации чего-то подобного на обычном языке, вы обнаруживаете, что это не подходит, поэтому вам нужно написать много дополнительного кода, чтобы охватить то, что эксперт в предметной области имел в виду под словом «тогда».

В Haskell вы может реализовать это как монаду с «then», переведенным в оператор связывания. Так, например, я однажды написал программу, в которой элемент должен был быть назначен из пулов в соответствии с определенными правилами. Для случая 1 вы взяли его из пула X. Если он был пуст, вы перешли к пулу Y. Для случая 2 вам нужно было взять его прямо из пула Y. И так для дюжины или около того случаев, включая те, где вы взяли наименее недавно использованный из пула X или Y. Я написал специальную монаду специально для этой работы, чтобы я мог написать:

case c of
   1: do {try poolX; try poolY}
   2: try poolY
   3: try $ lru [poolX, poolY]

Она работала очень хорошо.

Конечно, это включает в себя обычные модели секвенирования. Монада ввода-вывода - это модель, которая есть во всех других языках программирования; просто в Haskell это явный выбор, а не часть среды. Монада ST дает вам мутацию памяти ввода-вывода, но без фактического ввода и вывода. С другой стороны, монада State позволяет вам ограничить ваше состояние одним значением именованного типа.

О чем-то действительно ломающем голову см. этот пост в блоге о монаде обратного состояния. Состояние распространяется в направлении, противоположном «казни». Если вы думаете об этом как о монаде состояния, выполняющей одну инструкцию, за которой следует следующая, то «put» отправит значение состояния назад во времени к любому предыдущему «get». Что на самом деле происходит, так это то, что устанавливается взаимно рекурсивная функция, которая завершается только в том случае, если нет никаких парадоксов. Я не совсем уверен, где использовать такую ​​монаду, но она иллюстрирует мысль о том, что монады являются моделями вычислений.

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

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

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