Что именно делает вариант монадой в Scala?
Я знаю, что такое монады и как их использовать. То, что я не понимаю, , что делает, допустим, Option
монаду?
В Haskell монада Maybe
является монадой, поскольку она создается из класса Monad
(который имеет как минимум 2 необходимые функции return
и bind
, что делает класс Monad
, действительно, монадой).
Но в Scala у нас есть это:
sealed abstract class Option[+A] extends Product with Serializable { ... }
trait Product extends Any with Equals { ... }
Ничего не связано с монадой.
Если я создаю свой собственный класс в Scala, это будет монада по умолчанию? Почему бы и нет?
Ответы
Ответ 1
Monad
- это понятие, абстрактный интерфейс, если вы это сделаете, который просто определяет способ составления данных.
Option
поддерживает композицию через flatMap
, и это почти все, что необходимо для ношения "значка монады".
С теоретической точки зрения это также должно быть:
- поддерживает операцию
unit
(return
, в терминах Haskell), чтобы создать монаду из голого значения, которое в случае Option
является конструктором Some
- соблюдать монашеские законы
но это не строго соблюдается Scala.
Монады в scala - гораздо более слабая концепция, что в Haskell, и подход более практичен.
Единственное, что делает монады, с точки зрения языка - это способность быть использованным для понимания.
flatMap
является основным требованием, и вы можете дополнительно предоставить map
, withFilter
и foreach
.
Однако нет такой вещи, как строгое соответствие классу Monad
, например, в Haskell.
Вот пример: пусть определит нашу собственную монаду.
class MyMonad[A](value: A) {
def map[B](f: A => B) = new MyMonad(f(value))
def flatMap[B](f: A => MyMonad[B]) = f(value)
override def toString = value.toString
}
Как вы видите, мы реализуем только map
и flatMap
(ну и toString
как товар).
Поздравляем, у нас есть монада! Попробуйте:
scala> for {
a <- new MyMonad(2)
b <- new MyMonad(3)
} yield a + b
// res1: MyMonad[Int] = 5
Ницца! Мы не занимаемся фильтрацией, поэтому нам не нужно реализовывать withFilter
. Кроме того, поскольку мы даем значение, нам также не нужно foreach
. В принципе, вы выполняете все, что хотите, без строгих требований. Если вы попытаетесь выполнить фильтрацию для понимания, и вы не реализовали withFilter
, вы просто получите ошибку времени компиляции.
Ответ 2
Все, что (частично) реализует, путем утиного ввода, признак FilterMonadic
считается монадой в Scala. Это отличается от того, как монады представлены в Haskell или Monad
typeclass в scalaz. Однако, чтобы воспользоваться синтаксическим сахаром for
в Scala, объект должен выставить некоторые из методов, определенных в признаке FilterMonadic
.
Кроме того, в Scala эквивалент функции Haskell return
является ключевым словом yield
, используемым для создания значений из понимания for
. Desugaring yield
- это вызов метода map
"монады".
Ответ 3
То, как я выразился, заключается в том, что существует новое различие между монадами как шаблон дизайна и первоклассная абстракция. Haskell имеет последнее, в виде класса типа Monad
. Но если у вас есть тип, который (или может реализовать) монадические операции и подчиняется законам, это также монада.
В эти дни вы можете видеть монады как шаблон дизайна в библиотеках Java 8. Типы Optional
и Stream
в Java 8 поставляются со статическим методом of
, который соответствует методу Haskell return
и a flatMap
. Однако тип Monad
отсутствует.
Где-то между вами также есть подход с "утиным типом", как отвечает Ionuţ G. Stan. С# также имеет этот синтаксис LINQ не привязан к определенному типу, а скорее может использоваться с любым классом, который реализует определенные методы.