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

时间:2018-02-11 18:22:15

标签: scala monads monad-transformers scala-cats

我们正在创建我们自己的OptionT猫来理解,monad是如何工作的和monads转换流程。在创建我们自己的自定义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方法中的无案例?
  2. 执行代码时,出现运行时错误: Error:(26, 12) could not find implicit value for parameter M: usercases.mtransfomer.Monad[scala.concurrent.Future] addres <- WhateverOpt(repository.getAddressOption(user))
  3. 我们不确定错误,因为我们正在隐式创建optionTMonad,默认情况下,所有错误都在同一范围内。我们如何解决这两个问题?

    更新

    完整代码可在Github分支https://github.com/harmeetsingh0013/fp_scala/blob/master/src/main/scala/usercases/mtransfomer/Example5.scala

    上找到

3 个答案:

答案 0 :(得分:1)

  

如何处理WhateverOpt flatMap方法中的无案例?

当你flatMap超过None时,你会返回None。当您flatMap超过WhateverOpt[W[_], B]时,您想要返回pure的{​​{1}},代码中的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._
bool fileExists(const char* filename)
{
    FILE* fp = fopen(filename, "r");
    bool result = (fp != NULL);
    if (result) fclose(fp);
    return result; // <-- (cppcheck error) Resource leak: fp
}

答案 1 :(得分:1)

关于如何处理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 returnpure - monad的Future - 方法,因此对于一般情况,我们必须调用wMonad.pure(None)
  • 如果oa产生Some(a),我们可以将此a提供给f,然后立即解压缩以获得value类型W[Option[B]] 1}}。
  • 我们收到W[Option[B]](无论是empty还是WhateverOpt),我们可以将其打包到flatMap并从Monad[Option]方法返回。

我认为你想重新实现catsStdInstancesForOption只是为了好玩(它已经是in the libraryCommutativeMonad的东西是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 } } } ),但这是你怎么回事 - 建造它:

pure

请注意,1.0.1需要实现tailRecMfuture,并且不提供默认实现。

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

答案 2 :(得分:1)

  

如何处理WhateverOpt flatMap方法中的无案例?

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

  

执行代码时,获取运行时错误:错误:(26,12)可以   找不到参数M的隐含值:   usercases.mtransfomer.Monad [scala.concurrent.Future] addres&lt; -   WhateverOpt(repository.getAddressOption(用户))

发生此错误,因为在WhateverOpt构造函数中,我们知道我们的值为W[Option[A]],其中Option已经定义并由代码处理,但repository.getUserOption(1)返回{ {1}} Future[Option[User]]由通用参数Future处理,在这种情况下,我们需要定义如何处理W的monad。要解决该问题,我们需要实施Future而不是new Monad[Future],如下所示:

new Monad[Option]

我不确定我的回答,我在回答中提到过,但目前我的假设是这样的,对我而言,上面的代码工作正常。有关完整示例,请单击问题中提及的GitHub仓库。