Подтвердить что ты не робот

Scala Различия в функторах и монадах

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

4b9b3361

Ответ 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 и a PointedFunctor).

Все три элемента используются для определения 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. (Кроме того, в качестве побочной заметки есть некоторые законы, которые должны функционировать для функторов, аппликаций и монад).