【问题标题】:Scala: Create custom OptionT monad from cats for learningScala:从猫创建自定义 OptionT monad 以进行学习
【发布时间】:2018-07-21 22:30:55
【问题描述】:

我们正在创建自己的 OptionT 猫,以了解单子如何工作以及单子转换流程。在创建我们自己的自定义 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. 如何处理WhateverOptflatMap方法中的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提供完整代码

【问题讨论】:

  • 链接似乎不起作用,github 给出了 404 和一个 jedi-octocat。
  • 谢谢@AndreyTyukin,您现在可以查看。

标签: scala monads monad-transformers scala-cats


【解决方案1】:

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],方法是从猫中导入

import cats.instances.option._

【讨论】:

  • 你好@Gabriele,在导入import cats.instances.future._ import scala.concurrent.ExecutionContext.Implicits.global这个语句后,同样的错误正在发生。实际上,IntelliJ 并没有显示错误,在编写代码的过程中,这就是为什么我认为,这是运行时错误。
  • 你好 @Gabriele 我找到了答案,我们需要声明 Monad[Future] 而不是 Monad[Option] 因为,我们知道 Option 是我们自定义 WhateverOpt 的一部分但是,Future由泛型决定,这就是为什么在这种情况下,我们需要为隐式解析定义Monad[Future]
  • @GabrielePetronella NoneOption 的纯粹?
  • @AndreyTyukin 呜呜呜,实际上不是 :)
  • @HarmeetSinghTaara Monad[Future] 在哪里?
【解决方案2】:

关于如何处理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 产生oa 类型为Option[A] 的结果
  • 如果oa 变成None,那么我们无能为力,因为我们无法获得任何A 类型的实例来调用f。因此,立即返回Noneimmediately returnFuture-monad 的pure-方法,所以对于一般情况我们必须调用wMonad.pure(None)
  • 如果oa 产生Some(a),我们可以把这个af,然后立即解压它得到W[Option[B]] 类型的value
  • 一旦我们有了W[Option[B]](无论empty 与否),我们就可以将它包装到WhateverOpt 并从flatMap 方法返回。

我假设您想重新实现 Monad[Option] 只是为了好玩(它已经是 in the librarycatsStdInstancesForOptionCommutativeMonad),但您可以通过以下方式重新构建它:

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,并且没有为此提供默认实现。


我不想多说future 的必要导入,但最新版本有cats.instances.future,它提供了一个Monad 实例。再次检查,因为您似乎使用的是不同版本的猫(您的版本没有抱怨,因为您的 Option-monad 中缺少 tailRecM)。

【讨论】:

  • 谢谢@Andrey,提供解释。这对学习更多内容非常有帮助。但是根据我的代码和要求而不是new Monad[Option],我们需要实现new Monad[Future],因为我们知道WhateverOpt 包含Option 值,但是Future 由通用参数W 处理,这就是为什么,我们需要创建new Monad[Future]。更多详情,请查看 GitHub 代码。
  • @HarmeetSinghTaara 实现Monad[Future] 不会太难,因为Future 已经实现了flatMap(这是困难的部分)。但正如我在回答中所说:import cats.instances.future._ 应该足以得到catsStdInstancesForFuture,也就是你想要的Monad
  • 其实@Anrey 我对cats 库不太熟悉,从学习角度来看,我正在实现自己的Monad trait 和自定义OptionT monad。但是,是的,当然,每当我要探索 cats 库时,您的建议都会对我们有所帮助。
【解决方案3】:

WhateverOpt flatMap 方法中如何处理 None 情况?

@Gabriele Petronella@Andrey Tyukin 已经详细解释了这个答案。

执行代码时出现运行时错误:Error:(26, 12) could 找不到参数 M 的隐含值: usercases.mtransfomer.Monad[scala.concurrent.Future] 地址

出现这个错误是因为在WhateverOpt构造函数中我们知道我们的值是W[Option[A]],其中Option已经定义并由代码处理,但是repository.getUserOption(1)返回Future[Option[User]],其中Future被处理通过泛型参数W,在这种情况下,我们需要定义如何处理Future 的单子。为了解决这个问题,我们需要实现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 存储库。

【讨论】:

    猜你喜欢
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-12
    • 2019-11-08
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    相关资源
    最近更新 更多