Option[io.databaker.env.EnvValue],但类型F在类型上是不变的

Option[io.databaker.env.EnvValue], but type F is invariant in type

我有以下代码片段,它没有被编译:

trait Environment[F[_]] {
  def get(v: EnvVariable): F[Option[EnvValue]]
}

final class LiveBadEnvironment[F[_] : Sync] extends Environment[F] {
  override def get(v: env.EnvVariable): F[Option[env.EnvValue]] = None.pure[F]
}

编译器抱怨:

[error]  found   : F[None.type]
[error]  required: F[Option[io.databaker.env.EnvValue]]
[error]     (which expands to)  F[Option[io.databaker.env.EnvValue.Type]]
[error] Note: None.type <: Option[io.databaker.env.EnvValue], but type F is invariant in type _.
[error] You may wish to define _ as +_ instead. (SLS 4.5)
[error]   override def get(v: env.EnvVariable): F[Option[env.EnvValue]] = None.pure[F]

我做错了什么?

这里是问题的最小化示例,显示了引入 pure 扩展方法的导入:

scala> import cats.Applicative, cats.implicits._
import cats.Applicative
import cats.implicits._

scala> def foo[F[_]: Applicative]: F[Option[String]] = None.pure[F]
                                                            ^
   error: type mismatch;
    found   : F[None.type]
    required: F[Option[String]]
   Note: None.type <: Option[String], but type F is invariant in type _.
   You may wish to define _ as +_ instead. (SLS 4.5)

问题是 None.pure[F] 的类型被推断为 F[None.type],而没有预期的 return 类型影响推断。如果你 desugar 上下文绑定和扩展方法,类型推断将按你预期的那样工作:

scala> def foo[F[_]](implicit F: Applicative[F]): F[Option[String]] = F.pure(None)
def foo[F[_]](implicit F: cats.Applicative[F]): F[Option[String]]

你也可以写出 Option.empty[String].pure[F] 之类的东西——有很多方法可以 运行 解决这种问题(过于精确的类型推断),还有很多解决它的方法,你应该选择哪种主要是品味问题。

I have changed to override def get(v: env.EnvVariable): F[Option[env.EnvValue]] = F.pure(None) and got the error message not found: value F. What am I doing wrong?

考虑 F 这个名字在 implicit F: Applicative[F]

中的使用方式
def foo[F[_]](implicit F: Applicative[F]): F[Option[String]] = F.pure(None)
        |              |              |                        |
      type           value          type                 "type as value"

请注意 value 参数 F 如何与 type[=61] 具有 相同 名称=]参数F。现在在值 F 上调用方法看起来就像我们在类型

上调用方法一样
F.pure(None)

在 Scala 中使用点语法调用类型上的方法是不可能的,但从概念上讲这就是我们正在做的事情——我们希望传达调用“类型级”函数的想法。 naming convention 之所以有效,是因为值和类型存在于两个独立的宇宙中,因此我们可以重复使用相同的名称而不会发生冲突。例如,考虑为什么以下是合法的

scala> object f { def f[f](f: f): f = f }
     | val Int: Int = 42
object f
val Int: Int = 42              

现在使用上下文绑定 : 表示法

def foo[F[_]: Applicative]: F[Option[String]] = Applicative[F].pure(None)

我们没有隐式值参数的名称可以使用,所以我们不能使用上面的约定技巧和调用

F.pure(None)

再一次,因为类型上的点符号严格来说是非法的,所以我们改为使用带有 main 方法技巧的伴随对象

Applicative[F].pure(None)

这是可行的,因为 Applicative 伴侣有类似

的内容
Applicative {
  def apply[F[_]](implicit instance: Applicative[F]): Applicative[F] = instance
}

所以打电话

Applicative.apply[F]

或更短

Applicative[F]

returns 范围内的隐式 instance。在这一点上,我们确实有我们的 value 可以使用,所以点符号变得合法

Applicative[F].pure(None)
              |
    ok because invoked on a value

因此,您必须使用 Sync[F].pure(None) 而不是 F.pure(None) 进行调用,因为在您的特定情况下,您使用的是上下文边界。