繁体   English   中英

Scala:从猫中创建自定义OptionT monad以进行学习

[英]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

以下是我们遇到的问题:

  1. 如何处理WhateverOpt flatMap方法中的None情况?
  2. 执行代码时,出现运行时错误: 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
      }
    })
  }
}

想象一下WFuture 然后上面的代码说:

  • 等到包装value产生Option[A]类型的结果oa
  • 如果oa变成None ,那么我们无能为力,因为我们无法获得任何类型为A实例来调用f 因此,立即返回None immediately returnFuture -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需要实现puretailRecM ,并且不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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM