В отличие от функтора, монада может менять форму? - PullRequest
19 голосов
/ 09 декабря 2011

Мне всегда нравилось следующее интуитивное объяснение силы монады относительно функтора: монада может менять форму;функтор не может.

Например: length $ fmap f [1,2,3] всегда равен 3.

Однако с монадой length $ [1,2,3] >>= g часто не будет равно 3.Например, если g определено как:

g :: (Num a) => a -> [a]
g x = if x==2 then [] else [x]

, тогда [1,2,3] >>= g равно [1,3].

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

h :: (Monad m, Num a) => a -> m a

Классы типа MonadPlus или MonadZero имеют соответствующие нулевые элементы, которые можно использовать вместо [], но теперь у нас есть нечто большее, чем монада.

Я прав?Если так, есть ли способ выразить эту тонкость новичку в Haskell.Я хотел бы сделать мою любимую фразу «монады могут изменить форму», чуть более честной;если нужно.

Ответы [ 8 ]

16 голосов
/ 09 декабря 2011

Мне всегда нравилось следующее интуитивное объяснение силы монады относительно функтора: монада может менять форму;функтор не может.

Кстати, вам здесь не хватает тонкости.Ради терминологии я разделю Functor в смысле Хаскелла на три части: параметрический компонент, определяемый параметром типа и управляемый fmap, неизменяемые части, такие как конструктор кортежей в Stateи «форма», как и все остальное, например, выбор между конструкторами (например, Nothing против Just) или части, включающие параметры другого типа (например, среда в Reader).

Конечно, один Functor ограничен функциями отображения по параметрической части.

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

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

Я прав?Если так, есть ли способ выразить эту тонкость новичку в Haskell.Я хотел бы сделать мою любимую фразу «монады могут изменить форму», чуть более честной;если нужно.

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

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

8 голосов
/ 09 декабря 2011
Операции

Monad могут «изменять форму» значений до такой степени, что функция >>= заменяет конечные узлы в «дереве», которое является исходным значением, новой подструктурой, полученной из значения узла (дляподходящее общее понятие «дерево» - в случае списка «дерево» является ассоциативным).

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

Более просвещенная точка зрения можетрассмотреть fmap и join вместо >>=.Вместе с return любой способ дает эквивалентное определение монады.Однако в представлении fmap / join то, что здесь происходит, более понятно.Продолжая ваш пример списка, сначала g - это fmap ped по списку, в результате чего получается [[1],[],[3]].Тогда этот список join ed, который для списка просто concat.

7 голосов
/ 09 декабря 2011

То, что шаблон монады включает в себя некоторые особые экземпляры, которые допускают изменения формы, не означает, что каждый экземпляр может иметь изменения формы.Например, в монаде Identity доступна только одна «форма»:

newtype Identity a = Identity a
instance Monad Identity where
    return = Identity
    Identity a >>= f = f a

На самом деле, мне не ясно, что очень много монад имеют значимые «формы»: например, чтоозначает «форма» в монадах State, Reader, Writer, ST, STM или IO?

4 голосов
/ 09 декабря 2011

Ключевой комбинатор для монад - (>>=).Зная, что он составляет два монадических значения и читая его сигнатуру типа, сила монад становится более очевидной:

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

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

3 голосов
/ 09 декабря 2011

Функция с такой сигнатурой, как h, действительно не может делать много интересных вещей, кроме выполнения арифметики с ее аргументом. Итак, у вас там правильная интуиция.

Однако, это может помочь в поиске часто используемых библиотек для функций с похожими сигнатурами . Вы обнаружите, что наиболее общие из них, как и следовало ожидать, выполняют общие монады, например return, liftM или join. Кроме того, когда вы используете liftM или fmap, чтобы поднять обычную функцию в монадическую функцию, вы обычно получаете аналогичную общую сигнатуру, и это довольно удобно для интеграции чистых функций с монадическим кодом.

Чтобы использовать структуру, которую предлагает конкретная монада, вам неизбежно нужно использовать некоторые знания о конкретной монаде, в которой вы находитесь, для создания новых и интересных вычислений в этой монаде. Рассмотрим государственную монаду (s -> (a, s)). Не зная этого типа, мы не можем написать get = \s -> (s, s), но, не имея возможности получить доступ к состоянию, нет особого смысла в нахождении в монаде.

1 голос
/ 10 декабря 2011

Простейший тип функции, удовлетворяющий требованию, которое я могу себе представить, состоит в следующем:

enigma :: Monad m => m () -> m ()

Его можно реализовать одним из следующих способов:

enigma1 m = m -- not changing the shape

enigma2 _ = return () -- changing the shape

Это былоочень простое изменение - enigma2 просто отбрасывает форму и заменяет ее на тривиальную.Другой тип общего изменения - это объединение двух фигур:

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

Результат foo может иметь форму, отличную от a и b.

Третье очевидное изменениеформы, требующей полной мощи монады:

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

Форма join x обычно не совпадает с самой x.

Объединение этих примитивных измененийформы, можно получить нетривиальные вещи, такие как sequence, foldM и тому подобное.

0 голосов
/ 10 декабря 2011

Это не полный ответ, но у меня есть несколько слов по поводу вашего вопроса, которые не вписываются в комментарий.

Во-первых, Monad и Functor являются типами классов; они классифицируют типы . Поэтому странно говорить, что «монада может изменить форму, а функтор - нет». Я полагаю, что вы пытаетесь говорить о «монадическом значении» или, возможно, о «монадическом действии»: значении типа m a для некоторого Monad m вида * -> * и некоторого другого типа вида * , Я не совсем уверен, как назвать Functor f :: f a, полагаю, я бы назвал это «значением в функторе», хотя это не лучшее описание, скажем, IO String (IO - функтор).

Во-вторых, обратите внимание, что все монады обязательно являются функторами (fmap = liftM), поэтому я бы сказал, что вы наблюдаете разницу между fmap и >>= или даже между f и g, а не между Monad и Functor.

0 голосов
/ 09 декабря 2011

ли

h :: (Monad m, Num a) => a -> m a
h 0 = fail "Failed."
h a = return a

удовлетворить ваши потребности? Например,

> [0,1,2,3] >>= h
[1,2,3]
...