Добавление Haseell Monadic оператора привязки к Scala
В Haskell вы можете использовать оператор bind (>>=
) следующим образом:
repli :: [a] -> [a]
repli xs = xs >>= \x -> [x,x]
*Main> repli [1,2,3]
[1,1,2,2,3,3]
Я читал, что flatMap
есть оператор Scala bind:
def repli [A](xs: List[A]): List[A] =
xs.flatMap { x => List(x,x) }
scala> repli (List(1,2,3))
res0: List[Int] = List(1, 1, 2, 2, 3, 3)
Как педагогическое упражнение, я пытаюсь добавить поддержку >>=
к Scala:
class MyList[T](list: List[T]) {
def >>= [U](f: T => List[U]): List[U] = list.flatMap(f)
}
implicit def list2mylist[T](list: List[T]) = new MyList(list)
def repliNew [A](xs: List[A]): List[A] =
xs >>= { x: A => List(x,x) }
scala> repliNew (List(1,2,3))
res1: List[Int] = List(1, 1, 2, 2, 3, 3)
Это работает отлично, но только для списков. Я действительно хочу поддерживать любой класс с помощью метода flatMap
. Каков наилучший способ сделать это?
Ответы
Ответ 1
Scalaz делает следующее:
trait MA[M[_], A] {
def value: M[A]
def >>=(f: A => M[B])(implicit m: Monad[M]): M[B] =
m.bind(value, f)
}
С неявными преобразованиями от M[A]
до MA[M, A]
для всех M[_]
и A
:
implicit def ma[M[_], A](m: => M[A]): MA[M, A] = new MA[M, A] {
lazy val value = m
}
Вам просто нужен признак Monad
и его экземпляр для каждой монады, о которой вы заботитесь:
trait Monad[M[_]] {
def pure[A](a: => A): M[A]
def bind[A, B](m: M[A], f: A => M[B]): M[B]
}
Ответ 2
Как добавить синоним для flatMap
с неявным классом?
implicit class BindRich[A,B](fm:{def flatMap(a:A):B}) {
def >>=(a:A):B = fm.flatMap(a)
}
В этом случае в любом объекте, имеющем метод flatMap
, также будет >>=
.