Скала функтор и Монада различия
может ли кто-нибудь объяснить различия между функтором и монадой в контексте Scala?
5 ответов:
сама Scala действительно не подчеркивает
Functor
иMonad
плане, что много. Я думаю, с помощьюmap
является стороной функтора, используяflatMap
- это сторона монады.для меня смотреть и играть вокруг с scalaz до сих пор был лучшим способом получить представление об этих функциональных концепциях в контексте scala (по сравнению с контекстом haskell). Два года назад, когда я начал scala, код scalaz был для меня тарабарщиной, а несколько месяцев назад я начал искать опять же, и я понял, что это действительно чистая реализация этого конкретного стиля функционального программирования.
, например,
Monad
реализация показывает, что монада-указал функтор потому что он расширяетPointed
черта (а такжеApplicative
черта). Я приглашаю вас посмотреть на код. Он имеет ссылки в самом источнике, и это действительно легко следовать по ссылкам.таким образом, функторы являются более общими. Монады обеспечивают дополнительные элементы. Чтобы понять, что вы можете сделать, когда у вас есть функтор или когда у вас есть монада, вы можете посмотреть на
MA
вы увидите служебные методы, которым нужен неявный функтор (в частности, аппликативные функторы), такие как
sequence
и иногда методы, которые нуждаются в полной монаде, такие какreplicateM
.
С 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]
. Это действительно просто обратный способ смотреть на scalamap
метод, который (игнорируяCanBuildFrom
прочее) в основном:F[A] => (A => B) => F[B]
если у меня есть список строк, функция от строки до Int, то я, очевидно, могу создать список Ints. Это касается опции, потока и т. д. Они все функторы
что я нахожу интересным в этом, так это то, что вы можете сразу перейти к (неправильному) выводу, что функтор является "контейнером"
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)
несколько экземпляров функтора:
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 }
In 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, хороший практический пример того, что может означать написание программы внутри монады.
некоторое время назад я писал об этом: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.
монада-это мифическое существо с неуловимым пониманием и несколько метафоры, но я нашел это довольно легко понять, как только вы получите аппликативный функтор:
функтор позволяет применять функции к вещам в контексте. Но если функции, которые мы хотим применить уже в контексте? (И довольно легко закончить в этой ситуации, если у вас есть функции, которые принимают более одного параметра).
Теперь нам нужно что-то вроде функтора, но это также берет функции уже в контексте и применяет их к элементам в контексте. И вот что такое аппликативный функтор. Вот подпись:
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]
EDIT: забыл важную вещь, чтобы связать его: символ > > = называется связать и flatMap в Scala. (Также, как Примечание стороны, есть некоторые законы, которые функторы, прозрачна, и монады должны следовать, чтобы работать должным образом).
Лучшая статья, подробно излагающая эти два понятия, -"суть шаблона итератора " из блог Эрика Торреборре.
функтор
trait Functor[F[_]] { def fmap[A, B](f: A => B): F[A] => F[B] }
- один из способов интерпретации
Functor
описать его как вычисление значений типаA
.
Например:
List[A]
- это вычисление, возвращающее несколько значений типаA
(недетерминированных вычислений),Option[A]
для вычислений, которые вы можете или не можете иметь,Future[A]
вычисление значения типаA
что вы получите позже, и так далее.- другой способ представить это как какой-то "контейнер" для значений типа.
это основной слой, из которого вы определите:
PointedFunctor
(чтобы создать значение типаF[A]
) иApplic
(указать способapplic
, будучи вычисленным значением внутри контейнераF (F[A => B])
, чтобы применить значениеF[A]
),Applicative Functor
(агрегирование элементApplic
иPointedFunctor
).все три элемента используются для определения
Monad
.
Я думаю, что это большое сообщение в блоге поможет вам в первую очередь для
monad
. http://blog.enfranchisedmind.com/2007/08/a-monad-tutorial-for-ocaml/