我正在 scala 2.13 中构建我自己的用于教育目的的小型函数式编程库,在这样做的过程中,我无法满足用于 for 推导式的
map
操作的要求。
在这里,我使用
flatMap
和 pure
的组合将 map 实现为派生原语,这样我就不必每次定义 monad 实例时都为其提供实现,但不知何故,编译器对此不满意:
trait Monad[F[_]] {
def pure[A](a: A): F[A]
def flatMap[A, B](fa: F[A])(g: A => F[B]): F[B]
def map[A, B](fa: F[A])(f: A => B): F[B] =
flatMap(fa)(f andThen pure)
}
case class Id[A](get: A)
object Monad {
val idMonad = new Monad[Id] {
def pure[A](a: A) = Id(a)
def flatMap[A,B](ma: Id[A])(f: A => Id[B]) =
f(ma.get)
}
val example: Id[Unit] = for {
_ <- Id(())
} yield ()
编译器错误:
[error] value map is not a member of fp.Id[Unit]
[error] _ <- Id(())
[error] ^^^^^^
Error compiling project (Scala 2.13.13, JVM (17))
scala 版本:2.13.13
有没有人看到我所做的事情中有任何明显的错误,不会接受我对
map
的定义?
我已将 map 定义为 monad 实例
Id
的方法,它允许使用 for-compression,但完全违背了我不必为 map
提供实现的意图,因为 pure
和 flatMap
已实施。
case class Id[A](get: A) {
def flatMap[B](f: A => Id[B]): Id[B] = f(get)
// Adding map here will satisfy the requirement for for-comprehensions.
def map[B](f: A => B): Id[B] = Id(f(get))
}
我的目标是派生原语,例如地图,因为所有 Monad 都是函子。
正如评论中提到的,您没有在
map
类上实现 Id
方法。此外请注意,虽然您的 map
方法需要一个实例和两个类型参数,但对于要由编译器合成的 map
方法调用,您宁愿需要在实例上定义 map
方法。
到目前为止,您所做的是在 Scala 中实现“类型类模式”的一部分。用于在类型类实例和对象实例之间“桥接”的常见机制是使用 implicit class
,以及在隐式作用域中添加类型类实例,如以下示例所示:
trait Monad[F[_]] {
def pure[A](a: A): F[A]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def map[A, B](fa: F[A])(f: A => B): F[B] =
flatMap(fa)(f andThen pure)
}
case class Id[A](get: A)
object Monad {
implicit class MonadOps[F[_], A](fa: F[A])(implicit val M: Monad[F]) {
def flatMap[B](f: A => F[B]): F[B] = M.flatMap[A, B](fa)(f)
def map[B](f: A => B): F[B] = M.map[A, B](fa)(f)
}
implicit val idMonad: Monad[Id] = new Monad[Id] {
def pure[A](a: A): Id[A] = Id(a)
def flatMap[A, B](ma: Id[A])(f: A => Id[B]): Id[B] =
f(ma.get)
}
}
import Monad._
val fourtytwo = for {
twentyone <- Id(21)
} yield twentyone * 2
assert(fourtytwo.get == 42)
您可以在Scastie 上使用此代码本文