由ReaderT包装的带有Option和Some的Scala理解

时间:2019-03-25 11:28:44

标签: scala typeclass scala-cats for-comprehension subtyping

这是一个很好的示例:

  import cats.data.ReaderT
  import cats.instances.option._
  ...
  def f1:ReaderT[Option, Service, Int] =
    ReaderT(service => for {
      res <- Some(10)
    } yield res )

这是一个未编译的示例:

  def f2:ReaderT[Option, Service, Int] =
    for {
      res <- ReaderT((_:Service) => Some(10))
    } yield res

我收到以下错误:

  

错误:(53,11)找不到参数F的隐式值:   cats.Functor [某些]         res <-ReaderT((:Service)=> Some(10))错误:(53,11)没有足够的参数用于方法映射:(隐式F:   cats.Functor [Some])cats.data.Kleisli [Some,com.savdev.Service,Int]。   未指定的值参数F。         res <-ReaderT(((:Service)=> Some(10))

要解决第二个示例中的错误,我必须返回的不是Some,而是返回Option,它是Some的父级:

  def f2:ReaderT[Option, Service, Int] =
    for {
      res <- ReaderT((_:Service) => Option(10))
    } yield res

您能解释一下吗?为什么在第一个示例中,返回Some而不返回Option可以正常工作。为什么在第二个示例中同时未返回Some呢? Scala编译器是否可以选择编译案例,如第二个示例中所示?或其他解决方案。

2 个答案:

答案 0 :(得分:4)

  1. 在第一种情况下,它直接在map上调用Some,已知它会返回Option(子类Some为{{ 1}}),然后继续找到Option

  2. 在第二种情况下,函数的返回类型被推断为Functor[Option],并且编译器尝试查找Some[Int]类型类的实例,以便在{ {1}}(类型类为Functor[Some]的临时多态性),但这失败了,因为Reader没有函子。

主要问题是Functor不仅是类型Some的实例的构造函数(例如,在Haskell中也是如此),但实际上是(大多无用)类型Some,有时会搞乱类型推断/隐式解析。

如果要强制生成的类型为Option,请使用Some来构造Option[Int]Option(10)来构造Some

答案 1 :(得分:3)

尝试

import cats.syntax.option._

def f2: ReaderT[Option, Service, Int] =
  for {
    res <- ReaderT((_: Service) => 10.some)
  } yield res

最好使用x.somenone(或none[X])而不是Some(x)None。它们的类型为Option[X],而不是Some[X]None.type。有时可以改善类型推断。实际上,OptionFunctor而不是Some的实例。

查看https://blog.softwaremill.com/9-tips-about-using-cats-in-scala-you-might-want-to-know-e1bafd365f88中的“扩展方法构造函数”

在第一种情况下,您很幸运能够正确推断出类型。