Option является и функтором, и монадой?

Является ли Scala Option и монадой, и функтором?

Насколько я понимаю, функтор — это просто тип данных, который предоставляет следующий API:

Функтор:

  • wrap (или apply), который берет примитив и оборачивает его внутри функтора

  • map который берет функтор, разворачивает его, применяет какую-то функцию и перепаковывает его

Итак, Option — функтор. Потому что я могу применить Option к примитиву, дающему мне Option[T]. Я также могу map на Option получить то, что внутри функтора и переупаковать внутри Option.

Чем отличается монада? Я думал, что у монады также есть функция apply и функция map. Из эта статья я понял, что монада также имеет flatMap? Что определяется как просто map, но без переупаковки результата внутри монады? (Или это map без переупаковки результата внутри функтора?!)

Поскольку Option поставляет как map, так и flatMap, значит ли это, что Option является и функтором, и монадой?

Каждый Монада также является Функтор. Не только Option, а любой Монада. - Во-вторых, apply не из Функтор, а из аппликативный(что также означает, что каждый аппликативный также является Функтор, а каждый Монада также является апликативный). - В-третьих, да, монада определяется наличием также flatMap, которую лучше рассматривать как map, которая осознает внутренний контекст, или как map, за которой следует flatten. Но, ИМХО, лучший способ различать их — это типы, map[A, B](fa: F[A])(f: A => B): F[B] VS flatMap[A, B](fa: F[A])(f: A => F[B]): F[B].

Luis Miguel Mejía Suárez 29.05.2019 23:29

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

Luis Miguel Mejía Suárez 29.05.2019 23:31
Option сам по себе не является функтором или монадой. Функтор - это тройка (которая удовлетворяет законам функтора); некоторый тип (F), единичная функция ((A) => F[A]) и функция отображения ((F[A], A => B) => F[B]). Монада — это три кортежа (которые удовлетворяют законам монад); некоторый тип (M), единичная функция ((A) => M[A]) и функция flatMap (или привязка) ((M[A], A => M[B]) => M[B]).
marstran 29.05.2019 23:35

@marstran: Scala — это объектно-ориентированный язык, поэтому операции являются частью типа. В этом смысле можно сказать, что Option — это монада. unit — это конструктор, map — это, ну, Option.map, а flatMap — это Option.flatMap.

Jörg W Mittag 30.05.2019 01:35

@ JörgWMittag Я бы не согласился. Одно из основных различий между Классы типов и Подтип заключается в том, что ваш тип больше не это Х, а скорее имеет (уникальный) X, связанный с ним. Кроме того, независимо от того, как вы решите реализовать их в Скала(или на любом языке), это не меняет математического определения концепции. Теперь я намеренно решил опустить это в своем первом комментарии, потому что, даже если я сам считаю эти различия важными, мне было трудно их понять, когда я впервые изучал эти концепции (как мое личное мнение, так и коллег).

Luis Miguel Mejía Suárez 30.05.2019 02:05

@LuisMiguelMejíaSuárez Под «осведомленностью о внутренних контекстах» я думаю, что вы говорите, что flatMap использует функцию, которая отображает «неподнятый» или примитивный объект в поднятую монаду, тогда как в функторной функции map функтор сам отвечает за поднятие результат?

franklin 30.05.2019 02:13

@franklin не совсем, позвольте мне расширить то, что я имею в виду, в контексте. Большинство "монады"(кавычки предназначены для обсуждения того, что они на самом деле не монады), таких как Option, Either, List, IO, обозначаются как Контексты или Эффекты, потому что они инкапсулируют значения чистый, с которыми связан дополнительный побочный эффект. (этот процесс называется овеществление), например, Option инкапсулирует эффект неполного вычисления или в Другими словами, значение, которое может существовать, а может и не существовать. Надеясь, что это может прояснить, что такое контекст, давайте рассмотрим, почему я имел в виду "осведомленный".

Luis Miguel Mejía Suárez 30.05.2019 02:23

Возьмем следующий пример safeDiv(a: Double, b: Double): Option[Double] = if (b == 0) None else Some(a / b). Учитывая это, вы можете создать это val r = safeDiv(10, 5). Теперь у вас есть не Двойник, а возможный Двойник. Что само по себе не очень полезно, поскольку теперь вы не можете суммировать его или выполнять какие-либо другие вычисления. Сначала вы можете сказать, о, мне нужно только получить значение, используя getOrElse с безопасным значением по умолчанию (как 0). Но что, если вы прямо сейчас не знаете, какое значение по умолчанию вы хотите, но все же вам нужно оперировать значением, поэтому вам нужен способ преобразования, не выходя из ctx

Luis Miguel Mejía Suárez 30.05.2019 02:27

Для этой проблемы (чрезвычайно часто) есть простое решение: просто вызовите map[A, B](fa: F[A])(f: A => B): F[B], потому что map позаботится о контексте за вас, и просто примените функцию к внутреннему значению (в данном конкретном случае, если он существует). Таким образом, вы можете сделать val r2 = r.map(_ + 1). Отлично, но что, если вы хотите разделить результат на другое число, вы можете использовать map... но это оставит вас с Опция[Опция[Двойной]], что означает двойную вероятность пропуска, что я сомневаюсь, что вы хотите. Вам действительно нужно просто значение, если оба подразделения преуспели, или просто None.

Luis Miguel Mejía Suárez 30.05.2019 02:30

Введите flatMap[A, B](fa: F[A])(f: A => F[B]): F[B], в основном для этой проблемы (тоже очень часто) тоже есть простое решение. flatMap не только знает контекст начального значения, но также знает, что результат функции сопоставления вернет другой контекст, который необходим "развернуть" для предоставления окончательного результата. Таким образом, теперь вы можете сделать val r3 = r2.flatMap(safeDiv(_, 0)). Я надеюсь, что это ясно. - Кстати, ради любопытства мы говорим, что Монада — это Функтор, потому что map = flatMap(fa)(a => unit(f(a))) также flatMap = flatten(map(fa)(f))(сгладить карту ;))

Luis Miguel Mejía Suárez 30.05.2019 02:34

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

franklin 30.05.2019 03:09

@franklin Да, я бы просто изменил «Я могу получить ввод, который также заключен в контекст» на Я получу (это необязательно) ввод, который также заключен в контекст - «поэтому я разверну ввод, выполню некоторую функцию и оберну вывод в контексте» в конце, да, вы завернете результат в контекст, но результат функции также заключен в контекст, поэтому вам нужно его тоже развернуть. Но да, и map, и flatMap распространяют контекст. ИМХО, хорошим упражнением для их понимания является реализация их с нуля для различных типов, таких как Option, List и IO.

Luis Miguel Mejía Suárez 30.05.2019 03:25

Я нашел эти видео очень хорошим написано и очень четким.

Luis Miguel Mejía Suárez 30.05.2019 03:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
13
212
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Краткий ответ: Да.

Более длинный ответ: каждая монада является аппликативным функтором, и каждый аппликативный функтор является функтором. В объектно-ориентированных терминах: Monad <: Applicative <: Functor.

Другие вопросы по теме