Может ли кто-нибудь объяснить различия между Functor и Monad в контексте Scala?
Scala Различия в функторах и монадах
Ответ 1
Scala сам по себе не особо подчеркивает термины Functor
и Monad
. Я полагаю, что использование map
является стороной-функтором, используя flatMap
- сторону Монады.
Для меня, глядя и играя с scalaz, был лучший способ получить представление об этих функциональных концепциях в контексте scala (по сравнению с контекст haskell). Два года назад, когда я начал scala, код scalaz был для меня тарабарщиком, затем несколько месяцев назад я снова начал искать, и я понял, что это действительно чистая реализация этого конкретного стиля функционального программирования.
Например, реализация Monad
показывает, что монада - это остроконечная functor, потому что она расширяет черту Pointed
(а также черту Applicative
). Я приглашаю вас взглянуть на код. Он имеет связь в самом источнике, и он действительно легко отслеживает ссылки.
Таким образом, функторы более общие. Монады предоставляют дополнительные функции. Чтобы понять, что вы можете сделать, когда у вас есть функтор или когда у вас есть монада, вы можете посмотреть MA
Вы увидите утилиты, которым нужен неявный функтор (в частности, аппликативные функторы), например методы sequence
и sometime, для которых требуется полная монада, например replicateM
.
Ответ 2
Взяв scalaz в качестве контрольной точки, тип F[_]
(то есть тип F, который параметризуется каким-то одним типом) является функтором, если функция может быть поднята в него. Что это значит:
class Function1W[A, B](self: A => B) {
def lift[F[_]: Functor]: F[A] => F[B]
}
То есть, если у меня есть функция A => B
, функтор F[_]
, то теперь у меня есть функция F[A] => F[B]
. Это действительно просто обратный взгляд на метод scala map
, который (игнорируя материал CanBuildFrom
) в основном:
F[A] => (A => B) => F[B]
Если у меня есть список строк, функция от String до Int, то я, очевидно, могу создать список Ints. Это касается опции, потока и т.д. Все они являются функторами
То, что мне интересно, это то, что вы можете сразу перейти к (неправильному) выводу о том, что Functor является "контейнером" A
s. Это лишнее ограничение. Например, подумайте о функции X => A
. Если у меня есть функция X => A
и функция A => B
, то, очевидно, по составу у меня есть функция X => B
. Но теперь посмотрите на это так:
type F[Y] = X => Y //F is fixed in X
(X => A) andThen (A => B) is X => B
F[A] A => B F[B]
Таким образом, тип X = > A для некоторого фиксированного X также является функтором. В scalaz, функтор разработан как признак следующим образом:
trait Functor[F[_]] { def fmap[A, B](fa: F[A], f: A => B): F[B] }
следовательно, метод Function1.lift
выше реализован
def lift[F[_]: Functor]: F[A] => F[B]
= (f: F[A]) => implicitly[Functor[F]].fmap(f, self)
Несколько экземпляров functor:
implicit val OptionFunctor = new Functor[Option] {
def fmap[A, B](fa: Option[A], f: A => B) = fa map f
}
implicit def Functor1Functor[X] = new Functor[({type l[a]=X => a})#l] {
def fmap[A, B](fa: X => B, f: A => B) = f compose fa
}
В scalaz монада создается следующим образом:
trait Monad[M[_]] {
def pure[A](a: A): M[A] //given a value, you can lift it into the monad
def bind[A, B](ma: M[A], f: A => B): M[B]
}
Не очевидно, какая польза от этого может быть. Получается, что ответ "очень". Я нашел Дэниела Спивака Монады не Метафоры предельно ясны в описании того, почему это может быть, а также Тони Морриса в конфигурации через читателя monad, хороший практический пример того, что можно было бы назвать написанием вашей программы внутри монады.
Ответ 3
Лучшая статья, в которой подробно излагаются эти два понятия: Суть шаблона итератора "из Блог Эрика Торреборра.
Functor
trait Functor[F[_]] {
def fmap[A, B](f: A => B): F[A] => F[B]
}
- Один из способов интерпретации
Functor
- описать его как вычисление значений типаA
.
Например:
List[A]
- это вычисление, возвращающее несколько значений типаA
(недетерминированное вычисление),Option[A]
предназначен для вычислений, которые могут быть у вас или нет,Future[A]
- это вычисление значения типаA
, которое вы получите позже, и так далее.- Другой способ представить это как какой-то "контейнер" для значений типа A.
Это базовый уровень, из которого вы определяете:
-
PointedFunctor
(чтобы создать значение типаF[A]
) и -
Applic
(для предоставления методаApplic
, являющегося вычисленным значением внутри контейнераF (F[A => B])
, для применения к значениюF[A]
),Applicative Functor
(агрегацияApplic
и aPointedFunctor
).
Все три элемента используются для определения Monad
.
Ответ 4
Некоторое время назад я писал об этом: http://gabrielsw.blogspot.com/2011/08/functors-applicative-functors-and.html (я не эксперт)
Первое, что нужно понять, это тип "T [X]": это своего рода "контекст" (полезно кодировать вещи в типах, и с этим вы их "сочиняете" ). Но см. другие ответы: )
Хорошо, теперь у вас есть свои типы внутри контекста, скажите M [A] (A "внутри" M), и у вас есть простая функция f: A = > B... вы не можете просто идти вперед и примените его, потому что функция ожидает A, и у вас есть M [A]. Вам нужно каким-то образом "распаковать" содержимое M, применить функцию и "упаковать" ее снова. Если у вас есть "интимное" знание внутренних элементов M, вы можете это сделать, если вы обобщите его в черте, которую вы закончите с помощью
trait Functor[T[_]]{
def fmap[A,B](f:A=>B)(ta:T[A]):T[B]
}
И это точно, что такое функтор. Он трансформирует T [A] в T [B], применяя функцию f.
Монада - мифическое существо с неуловимым пониманием и множественными метафорами, но мне было легко понять, как только вы получите прикладной функтор:
Функтор позволяет нам применять функции к вещам в контексте. Но что, если функции, которые мы хотим применить, уже находятся в контексте? (И довольно легко закончить в этой ситуации, если у вас есть функции, которые принимают более одного параметра).
Теперь нам нужно что-то вроде Functor, но оно также принимает функции уже в контексте и применяет их к элементам в контексте. И это то, чем является аппликативный функтор. Вот подпись:
trait Applicative[T[_]] extends Functor[T]{
def pure[A](a:A):T[A]
def <*>[A,B](tf:T[A=>B])(ta:T[A]):T[B]
}
Пока все хорошо. Теперь идут монады: что, если теперь у вас есть функция, которая помещает вещи в контекст? Его подпись будет g: X = > M [X]... вы не можете использовать функтор, потому что он ожидает X = > Y, поэтому мы закончим с M [M [X]], вы не можете использовать аппликативный функтор, потому что ожидает функцию уже в контексте M [X = > Y].
Итак, мы используем монаду, которая принимает функцию X = > M [X] и что-то уже в контексте M [A] и применяет функцию к тому, что внутри контекста, упаковывая результат только в один контекст. Подпись:
trait Monad[M[_]] extends Applicative[M]{
def >>=[A,B](ma:M[A])(f:A=>M[B]):M[B]
}
Это может быть довольно абстрактно, но если вы думаете о том, как работать с "Option", он показывает вам, как создавать функции X = > Option [X]
РЕДАКТИРОВАТЬ: Забыл важную вещь, чтобы связать его: символ → = называется bind и является flatMap в Scala. (Кроме того, в качестве побочной заметки есть некоторые законы, которые должны функционировать для функторов, аппликаций и монад).
Ответ 5
Я думаю, что это замечательное сообщение в блоге поможет вам в первую очередь для monad
. http://blog.enfranchisedmind.com/2007/08/a-monad-tutorial-for-ocaml/