[英]Scala: Create custom OptionT monad from cats for learning
我们正在创建自己的猫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
flatMap
方法中的None情况? 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
如何处理WhateverOpt flatMap方法中的None情况?
当flatMap
覆盖None
,返回None
。 当你flatMap
超过WhateverOpt[W[_], B]
要返回pure
的话,这在你的代码将是M.pure(None)
。
执行代码时,出现运行时错误:
Error:(26, 12) could not find implicit value for parameter M: usercases.mtransfomer.Monad[scala.concurrent.Future] addres <- WhateverOpt(repository.getAddressOption(user))
这是一个编译时错误(而不是运行时错误),这是由于缺少Monad[Future]
实例。 为了在猫的作用域中获得Monad[Future]
的实例,您可以执行以下操作:
import cats.instances.future._
import scala.concurrent.ExecutionContext.Implicits.global
另外,您可以避免通过从猫中导入Monad[Option]
来声明自己的Monad[Option]
import cats.instances.option._
关于如何处理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
。 immediately return
是Future
-monad的pure
方法,因此对于一般情况,我们必须调用wMonad.pure(None)
。 oa
产生Some(a)
,我们可以将a
f
,然后立即将其解压缩以获得W[Option[B]]
类型的value
。 W[Option[B]]
(无论是否为empty
),我们都可以将其包装到WhateverOpt
并从flatMap
方法返回。 我假设您只是想重新实现Monad[Option]
只是为了好玩(它已经在库中了 ( 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
,并且不tailRecM
提供默认实现。
我不想说太多future
,但是最新版本有cats.instances.future ,它提供了Monad
实例。 再次检查这一点,因为它好像你正在使用不同版本的猫(因为缺少你的版本没有抱怨tailRecM
在您Option
-monad)。
如何处理WhateverOpt flatMap方法中的None情况?
@Gabriele Petronella和@Andrey Tyukin已经详细解释了这个答案。
执行代码时,出现运行时错误:错误:(26,12)找不到参数M的隐式值:usercases.mtransfomer.Monad [scala.concurrent.Future] addres <-WhateverOpt(repository.getAddressOption(user))
发生此错误的原因是,在WhateverOpt
构造函数中,我们知道我们的值为W[Option[A]]
,其中Option
已经定义并由代码处理,但是repository.getUserOption(1)
返回Future[Option[User]]
,其中Future
由通用参数W
处理,在这种情况下,我们需要定义如何处理Future
monad。 为了解决该问题,我们需要实现new Monad[Future]
而不是new Monad[Option]
,如下所示:
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
case None => M.pure(None)
}))
}
implicit val futureMonad = new Monad[Future] {
override def pure[A](a: A): Future[A] = Future.successful(a)
override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f)
override def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f)
}
val optionResult: WhateverOpt[Future, String] = for {
user <- WhateverOpt(repository.getUserOption(1))
addres <- WhateverOpt(repository.getAddressOption(user))
} yield addres.city
我不确定自己在回答中提到的内容,但是目前我的假设是这样,对于我来说,上面的代码可以正常工作。 有关完整示例,请单击问题中提到的GitHub存储库。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.