Действительно ли функциональная монада предлагает нечто большее, чем аппликативный функтор функции? Если да, то?

Что касается монады функций, я обнаружил, что (<*>) и _2 _ / _ 3_ имеют два поразительно похожих типа. В частности, (=<<) делает сходство более очевидным:

(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
(=<<) :: (a -> r -> b) -> (r -> a) -> (r -> b)

Это похоже на то, что и (<*>), и _7 _ / _ 8_ берут двоичную функцию и унарную функцию и ограничивают один из двух аргументов первого должен быть определен из другого через последний. В конце концов, мы знаем, что для функции аппликатив / монада

f <*> g = \x -> f x (g x)
f =<< g = \x -> f (g x) x 

И они выглядят настолько поразительно похожими (или симметричными, если хотите), что я не могу не думать о вопросе в названии.

Что касается того, что монады более мощные, чем аппликативные функторы, в твердой копии LYAH For a Несколько монад Подробнее главы говорится следующее:

[…] join нельзя реализовать, просто используя функции, которые предоставляют функторы и аппликативы.

т.е. join не может быть реализовано в терминах (<*>), pure и fmap.

Но как насчет упомянутой выше функции Applicative / mondad?

Я знаю, что join === (>>= id) и что для функциональной монады, которая сводится к \f x -> f x x, т.е. двоичная функция становится унарной, если один аргумент последней вводится как оба аргумента первой.

Могу я выразить это через (<*>)? Ну, вообще-то я думаю, что могу: разве flip ($) <*> f === join f не правильно? Разве flip ($) <*> f не реализация join, которая обходится без _21 _ / _ 22_ и return?

Однако, думая о аппликативной / монаде списка, я могу выразить join без явного использования _25 _ / _ 26_ и return (и даже не (<*>), fwiw): join = concat; так что, вероятно, реализация join f = flip ($) <*> f - это своего рода трюк, который на самом деле не показывает, полагаюсь я только на Applicative или также на Monad.


person Enlico    schedule 31.05.2021    source источник
comment
Комментарии не подлежат расширенному обсуждению; этот разговор был перемещен в чат.   -  person Machavity♦    schedule 08.06.2021


Ответы (2)


Когда вы реализуете join таким образом, вы используете знания о типе функции сверх того, что дает Applicative. Эти знания закодированы в использовании ($). Это оператор приложения, который является сутью функции. То же самое происходит с вашим примером списка: вы используете concat, который основан на знании природы списков.

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

person Fyodor Soikin    schedule 31.05.2021
comment
Федор, где, по вашему мнению, знание типа функции закодировано в ответе Уилла Несса? - person Enlico; 01.06.2021
comment
@Enlico id - это функция. (<*> id) не является полиморфной функцией, которую можно использовать с любым аппликативом. Аналогично только для функций (>>= id) и (id >>=) одинаковы. - person Bergi; 01.06.2021
comment
@Bergi id сродни спискам [], а не concat. что он работает - это именно то, что означает для functions app. и пн. имеют такую ​​же мощность. в противном случае, что мы говорим, что в целом у Monad больше возможностей, чем у App? конечно, но вопрос был не в этом. если бы вы могли выразить join для списков с <*> и [], я бы сказал, что это означало бы, что они имеют такую ​​же силу и там. - person Will Ness; 01.06.2021
comment
В общем, если вы можете использовать знание конкретной монады, вы имеете в виду определенный тип данных. как вы предлагаете использовать сопоставление с образцом. в Monad as Monad нет сопоставления с образцом, только для типа данных. как Монада, он знает только >>= и return. - person Will Ness; 01.06.2021
comment
Так обычно говорят. Вы можете сказать монада может быть, или монада списка, или монада состояния. - person Fyodor Soikin; 01.06.2021
comment
да, это так, и это прискорбно. :) Я имею в виду, когда мы говорим о монаде «возможно», мы имеем в виду особенности того, как Maybe реализует монаду, и это нормально. но здесь, в этом контексте, это сбивает с толку. - person Will Ness; 01.06.2021
comment
@WillNess Я не хотел сравнивать id с concat - person Bergi; 01.06.2021
comment
Пока не принято, так как я все еще не уверен. С одной стороны, этот ответ прекрасно объясняет, почему я могу работать без >>=, <*> или какой-либо другой абстракции с того момента, как я узнаю, каков фактический тип данных. Но, с другой стороны, он не дает прямого ответа на вопрос в заголовке. Кроме того, я начинаю понимать точку зрения Уилла Несса и хочу немного подумать над ней. - person Enlico; 01.06.2021
comment
@Enlico Я просто повторил в своем ответе то, что, по моему мнению, было давно принятой бесспорной частью знаний о Haskell. Я даже думаю, что видел это где-то здесь, на SO. - person Will Ness; 02.06.2021
comment
@FyodorSoikin, просто чтобы я мог понять, ваш ответ говорит, что функции Monad более мощные, чем Applicative? - person Will Ness; 02.06.2021
comment
@WillNess Он говорит, что конкретная монада, функции, обладает всей мощью функций. То же самое и с этим конкретным аппликативом. - person Bergi; 02.06.2021
comment
@Bergi Я вижу, что сейчас происходит. Под функциями вы подразумеваете любое содержательное определение, которое я могу написать на Haskell, и я действительно имел в виду комбинаторы, не осознавая этого. мой аргумент сводится к W = CSI, где S неизбежно. (или, вероятно, есть способы выразить S с помощью W). в любом случае это СК - это основа. - person Will Ness; 03.06.2021
comment
(Wfx = CSIfx = SfIx = fx (Ix) = fxx.) Использование точечной записи ничего не проясняет. лямбда-исчисление является полным по Тьюрингу (или что-то в этом роде). использование комбинаторных обозначений выявляет дополнительную структуру. (и, да, S = B(BW)(BBC)) - person Will Ness; 03.06.2021

edit2: Проблема с вопросом в том, что он расплывчатый. Он использует понятие (более мощное), которое вообще не определено и оставляет читателя в догадках относительно его значения. Таким образом, мы можем получить только бессмысленные ответы. Конечно, можно закодировать все, что угодно, используя весь имеющийся в нашем распоряжении арсенал Haskell. Это пустое заявление. И вопрос не в этом.

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

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

Итак, для списков, содержащих только fmap и app (<*>), мы можем выразить так много вычислений, и добавление join в наш арсенал действительно увеличивает его. Не так с функциями. join = W = CSI = flip app id. Конец.

Реализовав app f g x = (f x) (g x) = id (f x) (g x) :: (->) r (a->b) -> (->) r a -> (->) r b, у меня уже есть flip app id :: (->) r (r->b) -> (->) r b, с таким же успехом я мог бы назвать его join, так как тип подходит. Он уже существует, написал я это или нет. С другой стороны, от app fs xs :: [] (a->b) -> [] a -> [] b я не могу получить [] ([] b) -> [] b. Оба -> в (->) r (a->b) одинаковы; функции являются специальными.

(Кстати, в настоящий момент я не понимаю, как кодировать app явно списков, не выполняя фактического кодирования join. Использование понимания списков эквивалентно использованию concat; и concat не реализация join, это join).


join f = f <*> id

достаточно просто, так что нет никаких сомнений.


(edit: ну видимо еще были сомнения).

(=<<) = (<*>) . flip для функций. Это оно. это означает, что для функций Monad и Applicative Functor одинаковы. flip - это универсальный комбинатор. concat нет. Конечно, здесь есть определенное смешение с функциями. Но там нет конкретной функции управления функциями (например, concat - это конкретная функция управления списками) или где-либо еще, потому что функции непрозрачны.

Рассматриваемый как особый тип данных, он может подвергаться сопоставлению с образцом. Хотя как Монада она знает только о >>= и return. concat действительно использует сопоставление с образцом для выполнения своей работы. id нет.

id здесь сродни спискам [], а не concat. То, что это работает, означает, что функции, рассматриваемые как Applicative Functor или Monad, одинаковы. Конечно, в целом у Monad больше возможностей, чем у Applicative, но вопрос не в этом. Если бы вы могли выразить join для списков с <*> и [], я бы сказал, что это означало бы, что они имеют одинаковую силу и для списков.

В (=<<) = (<*>) . flip и flip, и (.) ничего не делают для функций, к которым они применяются. Таким образом, они ничего не знают о внутреннем устройстве этих функций. Например, foo = foldr (\x acc -> x+1) 0 правильно вычислит длину списка аргументов, если этот список был, например, [1,2]. Говоря this, опираясь на this, использует некоторые внутренние знания функции foo (то же самое, что и concat, используя внутреннее знание своих списков аргументов, через сопоставление с образцом). Но просто использовать базовые комбинаторы, такие как flip, (.) и т. Д., Нельзя.

person Will Ness    schedule 31.05.2021
comment
Я начинаю понимать вашу точку зрения, Уилл. Я также добавил тег комбинаторной логики, так как он наиболее актуален в зависимости от вашего ответа. - person Enlico; 01.06.2021
comment
Я не понимаю. И id, и flip относятся к функциям и не являются частью Applicative интерфейса. (Ну, id действительно работает для общих Category, но все еще не является частью Applicative.) - person dfeuer; 01.06.2021
comment
@dfeuer Я думаю, что суть ответа Уилла - это id здесь сродни спискам '[], а не concat, то есть если вы можете объединить <*> с [], чтобы получить join, то у вас будет эквивалентное доказательство этот аппликативный элемент и монада одинаковы для экземпляров списка. - person Iven Marquardt; 02.06.2021
comment
@dfeuer для функций, join = (id =<<) = (<*> id). если id разрешено слева, почему нельзя разрешить справа? Конечно, он работает только для функций, но это означает, что он работает только для функций. Истинная сила монады по сравнению с аппликативом с функциями - это репликация аргументов (join f x = f x x) (и в приложении она уже есть, app f g x = (f x) (g x), так что _5 _... (я не думал, что рассмотрение id как примитива будет спорным)). Истинная сила монады по сравнению с аппликативом для списков - это сопоставление вложенных шаблонов (join ((x:xs):ys) = x : join (xs:ys)). - person Will Ness; 02.06.2021
comment
... и, очевидно, мы не можем использовать app в качестве примитива для достижения этого. - person Will Ness; 02.06.2021
comment
(я имел в виду, что не могу добиться этого, используя только приложение в качестве примитива) @dfeuer - person Will Ness; 02.06.2021
comment
@dfeuer, не могли бы вы мне помочь, вы читаете ответ Федора о том, что монада функций более мощная, чем аппликативная? вы думаете, что это так? - person Will Ness; 02.06.2021
comment
@WillNess Как вы уже упомянули (если я правильно вас понимаю) функции выполняют двойную роль: рассматриваемые как тип данных, они образуют монаду. Но они также являются средством кодирования операций с монадой для всех экземпляров. Однако, как я вижу сейчас, из этой двойственности нельзя вывести, что тип функции сам по себе особенный. flip может быть более полиморфным, чем concat, но он по-прежнему использует сведения о стрелке функции ->. concat использует сопоставление с образцом как правило исключения []. Но flip использует приложение функции как правило исключения ->. Есть ли в этом смысл? - person Iven Marquardt; 03.06.2021
comment
@IvenMarquardt concat деконструирует свой аргумент путем сопоставления с образцом. flip такого не делает. конструктора данных ->, который нужно было бы исключить, не существует. реализовав app f g x = (f x) (g x) = id (f x) (g x) :: (->) r (a->b) -> (->) r a -> (->) r b, у меня уже есть flip app id :: (->) r (r->b) -> (->) r b, с таким же успехом я мог бы назвать это join, так как тип подходит. Он уже существует, написал я это или нет. OTOH, от app fs xs :: [] (a->b) -> [] a -> [] b, похоже, не получается [] ([] b) -> [] b. оба -> в (->) r (a->b) одинаковы; функции являются специальными. это все, что я знаю. - person Will Ness; 03.06.2021
comment
@IvenMarquardt (заметьте, я не сказал ничего нового. См. Также этот комментарий здесь). - person Will Ness; 03.06.2021
comment
@WillNess Таким образом, добавление join не влияет на тип функции, и это делает функции особенными. Я все еще не понимаю часть комбинаторной логики, но это на мне. Хотелось бы, чтобы по этому поводу была более широкая дискуссия, но этого нет, и, вероятно, это слишком много, чтобы просить. - person Iven Marquardt; 03.06.2021
comment
@dfeuer, если я могу обратиться к вам еще раз, я действительно не понимаю возражения. Аппликативный интерфейс состоит из двух функций; если бы мы могли приготовить join из двух из них, и только из двух, это доказало бы, что Monad = Applicative в целом. почему это вообще должно рассматриваться? в противном случае я отредактировал свой ответ, добавив больше материала (и оставил здесь больше комментариев, пытаясь прояснить, что я имел в виду), я не знаю, видели ли вы это; возможно, это решает некоторые проблемы? Я должен сказать, что меня сбивает с толку все это здесь. - person Will Ness; 08.06.2021
comment
@WillNess, утверждение действительно верно для Proxy. Proxy >>= f = Proxy = Proxy <*> Proxy. Это также кажется верным в некотором категорическом смысле для Identity, поскольку pure является изоморфизмом. x >>= f = f (runIdentity x) = runIdentity (f <$> x). Что касается функций, я просто не считаю это утверждение значимым. - person dfeuer; 08.06.2021
comment
@dfeuer ну, наконец, я погуглил и получил, например, это (и поддерживающий комментарий от Луки на несколько строк ниже). - person Will Ness; 08.06.2021
comment
Ах, теперь я могу увидеть. flip здесь видится как curry . (. swap) . uncurry? - person dfeuer; 08.06.2021
comment
@dfeuer для меня flip - это C. это примитивно. но да, наверное. - person Will Ness; 08.06.2021
comment
@dfeuer или, может быть, вы подчеркивали тот факт, что Hask - это CCC, а функции - это одновременно его морфизмы и экспоненты. (проблема со мной в том, что я лично почти не понимаю слов, которые использую там, поэтому я не сказал этого в ответе) - person Will Ness; 08.06.2021