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

标签 scala monads monad-transformers scala-cats

我们正在创建自己的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 中的 None 情况flatMap方法?
  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 上找到

最佳答案

关于如何处理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),我们可以把这个af,然后立即解压它以获取 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 需要实现 puretailRecM,并且不提供默认实现。


我不想过多谈论 future 所需的导入,但最新版本有cats.instances.future它提供了一个 Monad 实例。再次检查一下,因为您似乎使用的是不同版本的 cats(您的版本没有因为 Option-monad 中缺少 tailRecM 而提示)。

关于Scala:从猫中创建自定义 OptionT monad 进行学习,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48734872/

相关文章:

scala - 集合、仿函数和方程混淆

haskell - 将 monad 添加到变压器堆栈的中间

scala - 如何使用 `flatMap` 实现延迟计算的成本估算?

haskell - 与 monad 中的值进行模式匹配

haskell - 模块化程序设计 - 将 Monad Transformer 组合到 Monad 不可知函数中

scala - Akka Streams 中平衡和广播扇出的区别

scala - akka http配置中的空闲超时和请求超时有什么区别?

Haskell ReaderT Env IO 样板

java - 使用phoenix在Hbase上保存数据框

java - 为什么不能用Java声明Monad接口(interface)?