Вариант [io.databaker.env.EnvValue], но тип F является инвариантным по типу - PullRequest
3 голосов
/ 21 июня 2020

У меня есть следующий фрагмент кода, который не компилируется:

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]

Что я делаю не так?

Ответы [ 2 ]

3 голосов
/ 21 июня 2020

Вот минимизированный пример проблемы, который показывает импорт, который вызывает метод расширения 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] , без влияния ожидаемого возвращаемого типа на вывод. Если вы обессахариваете привязку контекста и метод расширения, вывод типа будет работать так, как вы ожидаете:

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], et c. - есть много способов столкнуться с подобной проблемой (слишком точный вывод типа), и множество способов ее решения, и то, что вы должны выбрать, по большей части дело вкуса.

2 голосов
/ 21 июня 2020

Я переопределил def get(v: env.EnvVariable): F[Option[env.EnvValue]] = F.pure(None) и получил сообщение об ошибке not found: value F. Что я делаю не так?

Подумайте, как имя F используется в implicit F: Applicative[F]

def foo[F[_]](implicit F: Applicative[F]): F[Option[String]] = F.pure(None)
        |              |              |                        |
      type           value          type                 "type as value"

Обратите внимание, как значение параметр F имеет то же имя , что и параметр type F. Теперь вызов метода для значения F выглядит так, как будто мы вызываем метод для типа

F.pure(None)

Вызов метода для типа с использованием точечного синтаксиса невозможен в Scala, но концептуально это это то, что мы делаем - мы будем sh, чтобы передать идею вызова функции "уровня типа". Это соглашение об именах работает, потому что значения и типы находятся в двух разных юниверсах, поэтому мы можем повторно использовать одно и то же имя без конфликтов. Например, подумайте, почему следующее является допустимым:

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)

у нас нет имени неявного параметр value для работы, поэтому мы не можем использовать вышеуказанный трюк с соглашением и вызывать

F.pure(None)

, потому что, опять же, точечная нотация в типах строго говоря недопустима, поэтому вместо этого мы используем сопутствующий объект с уловкой основного метода

Applicative[F].pure(None)

Это работает, потому что Applicative companion имеет что-то вроде

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

, поэтому вызов

Applicative.apply[F]

или короче

Applicative[F]

возвращает неявное instance в объеме. На данный момент у нас есть значение , с которым мы можем работать, и поэтому точечная нотация становится допустимой

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

Следовательно, вы должны вызывать Sync[F].pure(None) вместо F.pure(None), потому что в в вашем конкретном случае вы используете границы контекста.

...