Объясняя кому-то, что такое класс типов X, я стараюсь найти хорошие примеры структур данных, которые в точности X.
Итак, я прошу примеры для:
- Конструктор типа, который не является Functor.
- Конструктор типа, который является Functor, но не Applicative.
- Конструктор типа, который является Аппликативным, но не Монадой.
- Конструктор типа, который является Монадой.
Я думаю, что есть множество примеров Монады повсюду, но хороший пример Монады с некоторым отношением к предыдущим примерам может завершить картину.
Я ищу примеры, которые были бы похожи друг на друга, отличаясь только аспектами, важными для принадлежности к определенному классу типов.
Если бы кто-нибудь смог подобрать пример Arrow где-нибудь в этой иерархии (это между Applicative и Monad?), Это тоже было бы здорово!
haskell
monads
functor
applicative
Rotsor
источник
источник
* -> *
) , для которого не существует не подходитfmap
?a -> String
, не функтор.a -> String
- математический функтор, но не ХаскеллFunctor
, чтобы быть ясным.(a -> b) -> f b -> f a
Ответы:
Конструктор типа, который не является Functor:
Из него можно сделать контравариантный функтор, но не (ковариантный) функтор. Попробуйте написать,
fmap
и у вас ничего не получится. Обратите внимание, что версия контравариантного функтора обратная:Конструктор типа, который является функтором, но не Applicative:
У меня нет хорошего примера. Да
Const
, но в идеале я бы хотел конкретный немоноид, и ни о чем не могу думать. Все типы в основном числовые, перечисления, продукты, суммы или функции, когда вы приступаете к этому. Вы можете увидеть ниже свиновод, и я не согласен с тем,Data.Void
является ли этоMonoid
;Поскольку
_|_
в Хаскеле это является юридической ценностью и фактически единственной юридической ценностьюData.Void
, это соответствует правилам Моноида. Я не уверен, чтоunsafeCoerce
с этим делать, потому что ваша программа больше не гарантирует, что не нарушит семантику Haskell, как только вы используете какую-либоunsafe
функцию.Обратитесь к Haskell Wiki за статьей о нижних ( ссылка ) или небезопасных функциях ( ссылка ).
Интересно, возможно ли создать такой конструктор типов, используя более богатую систему типов, такую как Agda или Haskell с различными расширениями.
Конструктор типа, который является Аппликативным, но не Монадой:
Вы можете сделать аппликатив из этого, например:
Но если вы сделаете это монадой, вы можете получить несоответствие размеров. Я подозреваю, что подобные примеры на практике редки.
Конструктор типа, который является Монадой:
О Стрелках:
Спрашивать, где находится Стрелка в этой иерархии, все равно, что спрашивать, что это за форма "красная". Обратите внимание на несоответствие:
но,
источник
Either a
в качестве примера для последнего случая, так как это легче понять.ZipList
._|_
Обитает в каждом типе *, но суть вVoid
том, что вам придется наклониться назад, чтобы создать его, или вы уничтожили его значение. Вот почему это не экземпляр Enum, Monoid и т. Д. Если он у вас уже есть, я с удовольствием позволю вам объединить их вместе (давая вамSemigroup
)mempty
, но я не даю инструментов для явного построения значения типаVoid
вvoid
, Вы должны зарядить пистолет, направить его на ногу и самостоятельно нажать на курок.Мой стиль может быть ограничен моим телефоном, но здесь идет.
не может быть Функтором. Если бы это было, мы бы
и Луна будет сделана из зеленого сыра.
между тем
это функтор
но не может быть аппликативным, иначе мы бы
и Зеленый будет сделан из лунного сыра (что может произойти, но только позже вечером).
(Дополнительное примечание:
Void
посколькуData.Void
это пустой тип данных. Если вы попытаетесь использовать,undefined
чтобы доказать, что это моноид, я буду использовать,unsafeCoerce
чтобы доказать, что это не так.)радостно,
является аппликативным во многих отношениях, например, как это было бы у Дейкстры,
но это не может быть монадой. Чтобы понять, почему нет, обратите внимание, что возвращение должно быть постоянно
Boo True
илиBoo False
, и, следовательно, чтоне может удержаться.
О да я чуть не забыл
это монада Катайся сам.
Самолет, чтобы поймать ...
источник
mempty
._|_
Я полагаю, что другие ответы пропустили несколько простых и распространенных примеров:
Конструктор типа, который является Functor, но не Applicative. Простой пример - пара:
Но нет способа определить его
Applicative
экземпляр без наложения дополнительных ограничений наr
. В частности, нет способа, как определитьpure :: a -> (r, a)
для произвольногоr
.Конструктор типа, который является Аппликативным, но не Монадой. Хорошо известным примером является ZipList . (Это
newtype
обертка списков и предоставляет им другойApplicative
экземпляр.)fmap
определяется обычным способом. Ноpure
и<*>
определяются какпоэтому
pure
создает бесконечный список путем повторения заданного значения и<*>
архивирует список функций со списком значений - применяет i-ю функцию к i- му элементу. (Стандарт<*>
на[]
производит все возможные комбинации применения I ую функции J -му элемент.) Но нет разумного способа , как определить монады (см этого поста ).Как стрелки вписываются в иерархию функтор / аппликатив / монад? Видите, идиомы не обращают внимания, стрелки дотошны, монады неразборчивы от Сэма Линдли, Филиппа Уодлера, Джереми Яллопа. MSFP 2008. (Они называют аппликативные функторы идиомами .) Аннотация:
источник
((,) r)
что функтор не аппликативен; но это только потому, что вы не можете определитьpure
для всехr
сразу. Следовательно, это причудливость языковой лаконичности, попытки определить (бесконечный) набор аппликативных функторов с одним определениемpure
и<*>
; в этом смысле, как представляется , не будет ничего математически глубоко об этом контрпример , так как для любого бетонаr
,((,) r)
может быть аппликативном функтор. Вопрос: Можете ли вы вспомнить функтор КОНКРЕТНЫЙ, который не является аппликативным?Хороший пример для конструктора типа, который не является функтором
Set
: Вы не можете реализоватьfmap :: (a -> b) -> f a -> f b
, потому что без дополнительного ограниченияOrd b
вы не можете создатьf b
.источник
fmap
является просто вопросом языка / реализации. Кроме того, можно обернутьSet
в монаду продолжения, которая делает из нее монаду со всеми ожидаемыми свойствами, см. Этот вопрос (хотя я не уверен, что это можно сделать эффективно).b
может быть неразрешимым, в этом случае вы не можете определитьfmap
!Functor
экземпляр сOrd
ограничением, но это может быть возможно при другом определенииFunctor
или лучшей языковой поддержке. На самом деле с ConstraintKinds можно определить класс типа, который можно параметризовать следующим образом.ord
ограничение, тот факт, что aSet
не может содержать повторяющиеся записи, означает, что это может изменитьfmap
контекст. Это нарушает закон ассоциативности.Я хотел бы предложить более систематический подход к ответу на этот вопрос, а также показать примеры, в которых не используются какие-либо специальные приемы, такие как «нижние» значения или бесконечные типы данных или что-либо подобное.
Когда конструкторы типов не могут иметь экземпляры классов типов?
В общем, есть две причины, по которым конструктор типов может не иметь экземпляра определенного класса типов:
Примеры первого типа проще, чем второго типа, потому что для первого нам нужно просто проверить, можно ли реализовать функцию с заданной сигнатурой типа, в то время как для второго типа мы должны доказать, что реализация отсутствует мог бы удовлетворить законы.
Конкретные примеры
Конструктор типа, который не может иметь экземпляр functor, потому что тип не может быть реализован:
Это контрафунктор, а не функтор, относительно параметра типа
a
, потому что он находитсяa
в контравариантной позиции. Невозможно реализовать функцию с сигнатурой типа(a -> b) -> F z a -> F z b
.Конструктор типа, который не является законным функтором, даже если сигнатура типа
fmap
может быть реализована:Любопытный аспект этого примера заключается в том, что мы можем реализовать
fmap
правильный тип, даже если онF
не может быть функтором, потому что он используетсяa
в противоположной позиции. Таким образом, эта реализация,fmap
показанная выше, вводит в заблуждение - даже если она имеет правильную сигнатуру типа (я считаю, что это единственно возможная реализация этой сигнатуры типа), законы функторов не выполняются. Например,fmap id
≠id
, потому чтоlet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
есть123
, ноlet (Q(f,_)) = id (Q(read,"123")) in f "456"
есть456
.На самом деле,
F
это всего лишь профессор, он не является ни функтором, ни контрафунктором.Законный функтор, который не является аппликативным, потому что сигнатура типа
pure
не может быть реализована: возьмите монаду Writer(a, w)
и удалите ограничение, котороеw
должно быть моноидом. Тогда невозможно построить значение типа(a, w)
изa
.Функтор , который не аппликативен , так как тип подпись
<*>
не может быть реализована:data F a = Either (Int -> a) (String -> a)
.Функтор, который не является законно-аппликативным, хотя могут быть реализованы методы класса типов:
Конструктор типов
P
является функтором, потому что он используетсяa
только в ковариантных позициях.Единственная возможная реализация сигнатуры типа
<*>
- это функция, которая всегда возвращаетNothing
:Но эта реализация не удовлетворяет закону тождества для аппликативных функторов.
Applicative
не является,Monad
потому что сигнатура типаbind
не может быть реализована.Я не знаю таких примеров!
Applicative
но неMonad
потому, что законы не могут быть выполнены, даже если подпись типаbind
может быть реализована.Этот пример вызвал немало дискуссий, поэтому можно с уверенностью сказать, что доказать правильность этого примера непросто. Но несколько человек подтвердили это независимо разными способами. См. `Данные PoE a = Пусто | Пара монад? для дополнительного обсуждения.
Несколько громоздко доказать, что законного
Monad
примера нет. Причиной немонадного поведения является то, что не существует естественного способа реализации,bind
когда функцияf :: a -> B b
может возвращатьNothing
илиJust
для других значенийa
.Возможно, более ясно рассмотреть
Maybe (a, a, a)
, что также не является монадой, и попытаться реализоватьjoin
для этого. Вы обнаружите, что не существует интуитивно разумного способа реализацииjoin
.В случаях, обозначенных как
???
, кажется очевидным, что мы не можем производитьJust (z1, z2, z3)
разумным и симметричным способом из шести различных значений типаa
. Конечно, мы могли бы выбрать какое-то произвольное подмножество этих шести значений, например, всегда брать первое непустое значение,Maybe
но это не удовлетворяло бы законам монады. ВозвращениеNothing
также не будет соответствовать законам.bind
но не соответствует законам идентичности.Обычная древовидная монада (или «дерево с ветвями в форме функтора») определяется как
Это бесплатная монада над функтором
f
. Форма данных представляет собой дерево, где каждая точка ветвления является «функторной» из поддеревьев. Стандартное двоичное дерево будет получено сtype f a = (a, a)
.Если мы изменим эту структуру данных, сделав также листья в форме функтора
f
, мы получим то, что я называю «полумонадой» - она имеетbind
то, что удовлетворяет законам естественности и ассоциативности, но ееpure
метод не соответствует одному из законов идентичности. «Полумонады - это полугруппы в категории эндофункторов, в чем проблема?» Это тип классаBind
.Для простоты я определяю
join
метод вместоbind
:Прививка веток стандартная, но прививка листьев нестандартная и дает а
Branch
. Это не проблема для закона ассоциативности, но нарушает один из законов идентичности.Когда у полиномиальных типов есть монады?
Ни один из функторов
Maybe (a, a)
иMaybe (a, a, a)
не может быть дан законныйMonad
экземпляр, хотя они, очевидноApplicative
.Эти функторы не имеют никаких трюков - нет
Void
или вbottom
любом месте, не сложно лени / строгости, ни бесконечных структуры, а также какие - либо ограничения класса типа.Applicative
Экземпляр полностью стандартны. Функцииreturn
иbind
могут быть реализованы для этих функторов, но не будут удовлетворять законам монады. Другими словами, эти функторы не являются монадами, потому что отсутствует определенная структура (но непросто понять, чего именно не хватает). Например, небольшое изменение в функторе может превратить его в монаду:data Maybe a = Nothing | Just a
это монада. Другой подобный функторdata P12 a = Either a (a, a)
- также монада.Конструкции для полиномиальных монад
В общем, вот некоторые конструкции, которые производят законные
Monad
s из полиномиальных типов. Во всех этих конструкцияхM
есть монада:type M a = Either c (w, a)
гдеw
любой моноидtype M a = m (Either c (w, a))
гдеm
какая-то монада иw
есть какой-нибудь моноидtype M a = (m1 a, m2 a)
гдеm1
иm2
какие монадыtype M a = Either a (m a)
гдеm
какая-то монадаПервая конструкция есть
WriterT w (Either c)
, вторая конструкция естьWriterT w (EitherT c m)
. Третья конструкция является компонентным произведением монад:pure @M
определяется как компонентное произведениеpure @m1
иpure @m2
, иjoin @M
определяется путем исключения данных о перекрестном произведении (напримерm1 (m1 a, m2 a)
, отображаетсяm1 (m1 a)
путем исключения второй части кортежа):Четвертая конструкция определяется как
Я проверил, что все четыре конструкции производят законные монады.
Я предполагаю, что нет никаких других конструкций для полиномиальных монад. Например, функтор
Maybe (Either (a, a) (a, a, a, a))
не получается ни через одну из этих конструкций и поэтому не является монадическим. Тем не менее,Either (a, a) (a, a, a)
это монадическая потому она изоморфна произведению трех монадa
,a
иMaybe a
. Кроме того,Either (a,a) (a,a,a,a)
является монадическим, потому что оно изоморфно произведениюa
иEither a (a, a, a)
.Четыре конструкции, показанные выше, позволят нам получить любую сумму любого количества продуктов любого числа
a
, например,Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
и так далее. Все такие конструкторы типов будут иметь (хотя бы один)Monad
экземпляр.Конечно, еще предстоит выяснить, какие варианты использования могут существовать для таких монад. Другая проблема заключается в том, что
Monad
экземпляры, полученные с помощью конструкций 1-4, как правило, не являются уникальными. Например, конструктору типаtype F a = Either a (a, a)
можно датьMonad
экземпляр двумя способами: конструкцией 4 с использованием монады(a, a)
и конструкцией 3 с использованием изоморфизма типовEither a (a, a) = (a, Maybe a)
. Опять же, поиск вариантов использования для этих реализаций не сразу очевиден.Остается вопрос - учитывая произвольный тип данных полинома, как распознать, есть ли у него
Monad
экземпляр. Я не знаю, как доказать, что нет других конструкций для полиномиальных монад. Я не думаю, что какая-либо теория существует до сих пор, чтобы ответить на этот вопрос.источник
B
, это монада. Можете ли вы дать контрпример к этой привязкеPair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Empty
?f
такое, котороеf x
есть,Empty
ноf y
естьPair
, и на следующем шаге оба являютсяPair
. Я вручную проверил, что законы не действуют ни для этой реализации, ни для какой-либо другой реализации. Но это большая работа, чтобы сделать это. Хотелось бы, чтобы был более простой способ понять это!Maybe
потомуMaybe
что не содержит разных значений, о которыхa
нужно беспокоиться.Monad
Экземпляр состоит из функций ,return
иbind
что законы , удовлетворяют. Есть две реализацииreturn
и 25 реализаций,bind
которые соответствуют требуемым типам. Прямым расчетом можно показать, что ни одна из реализаций не удовлетворяет законам. Чтобы сократить количество требуемой работы, я использовалjoin
вместо этогоbind
и сначала использовал законы об идентичности. Но это было немало работы.Traversable
нужно.m (Either a (m a))
преобразуется с помощьюpure @m
вm (Either (m a) (m a))
. Тогда тривиальноEither (m a) (m a) -> m a
, и мы можем использоватьjoin @m
. Это была реализация, для которой я проверил законы.