我们正在创建自己的OptionT
猫的理解,单子(monad)如何工作以及单子(monad)转换流程。在创建我们自己的自定义 monad 时遇到一些错误。下面第一件事是我们的代码:
case class WhateverOpt[W[_], A] (value: W[Option[A]]) {
def map[B] (f: A => B) (implicit M: Monad[W]): WhateverOpt[W, B] =
WhateverOpt(M.map(value)(_.map(f)))
def flatMap[B] (f: A => WhateverOpt[W, B]) (implicit M: Monad[W]): WhateverOpt[W, B] =
WhateverOpt(M.flatMap(value)(optA => optA match {
case Some(v) => f(v).value
}))
}
implicit val optionTMonad = new Monad[Option] {
override def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
override def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f)
}
val optionResult = for {
user <- WhateverOpt(repository.getUserOption(1))
addres <- WhateverOpt(repository.getAddressOption(user))
} yield addres.city
以下是我们陷入困境的要点:
- 如何处理
WhateverOpt
中的 None 情况flatMap
方法? - 执行代码时,出现运行时错误:
Error:(26, 12) could not find implicit value for parameter M: usercases.mtransfomer.Monad[scala.concurrent.Future] addres <- WhateverOpt(repository.getAddressOption(user))
我们不确定错误,因为我们正在创建 optionTMonad
隐式且默认情况下,所有内容都在同一范围内。我们该如何解决这两个问题呢?
更新
完整代码可在 Github 分支 https://github.com/harmeetsingh0013/fp_scala/blob/master/src/main/scala/usercases/mtransfomer/Example5.scala 上找到
最佳答案
关于如何处理None
:
case class WhateverOpt[W[_], A] (value: W[Option[A]]) {
def map[B] (f: A => B) (implicit M: Monad[W]): WhateverOpt[W, B] =
WhateverOpt(M.map(value)(_.map(f)))
def flatMap[B]
(f: A => WhateverOpt[W, B])
(implicit wMonad: Monad[W])
: WhateverOpt[W, B] = {
WhateverOpt(wMonad.flatMap(value) { (oa: Option[A]) =>
oa match {
case None => wMonad.pure(None)
case Some(a) => f(a).value
}
})
}
}
想象一下W
是 future
。那么上面的代码表示:
- 等待,直到包装的
value
产生Option[A]
类型的结果oa
- 如果
oa
结果是None
,那么我们无能为力,因为我们无法获取任何A
类型的实例命令调用f
。因此,立即返回None
。立即返回
是Future
-monad的pure
方法,因此对于一般情况,我们必须调用wMonad.pure (无)
。 - 如果
oa
产生Some(a)
,我们可以把这个a
给f
,然后立即解压它以获取W[Option[B]]
类型的value
。 - 一旦我们有了
W[Option[B]]
(无论是否为空
),我们就可以将其包装到WhateverOpt
中并返回来自flatMap
方法。
我假设您只是为了好玩而想重新实现 Monad[Option]
(它已经是 in the library (catsStdInstancesForOption
是一个 CommutativeMonad
>),但您可以通过以下方式重新构建它:
implicit val optionTMonad = new Monad[Option] {
override def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f)
def pure[A](a: A): Option[A] = Some(a)
def tailRecM[A, B](a: A)(f: (A) => Option[Either[A, B]]): Option[B] = {
f(a) match {
case Some(Left(nextA)) => tailRecM(nextA)(f)
case Some(Right(res)) => Some(res)
case None => None
}
}
}
请注意,1.0.1 需要实现 pure
和 tailRecM
,并且不提供默认实现。
我不想过多谈论 future
所需的导入,但最新版本有cats.instances.future它提供了一个 Monad 实例。再次检查一下,因为您似乎使用的是不同版本的 cats(您的版本没有因为 Option
-monad 中缺少 tailRecM
而提示)。
关于Scala:从猫中创建自定义 OptionT monad 进行学习,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48734872/