Скала функтор и Монада различия


может ли кто-нибудь объяснить различия между функтором и монадой в контексте Scala?

5 59

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]. Это действительно просто обратный способ смотреть на scala map метод, который (игнорируя CanBuildFrom прочее) в основном:

F[A] => (A => B) => F[B]

если у меня есть список строк, функция от строки до Int, то я, очевидно, могу создать список Ints. Это касается опции, потока и т. д. Они все функторы

что я нахожу интересным в этом, так это то, что вы можете сразу перейти к (неправильному) выводу, что функтор является "контейнером"As. это излишнее ограничение. Например, подумайте о функции 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/