Почему этот код зависит от `withFilter` или это ошибка? - PullRequest
0 голосов
/ 26 апреля 2018

Я работаю с кошками над проектом и не могу понять, почему эта странная ошибка компилятора возникает в этом коде:

с :Int не компилируется

scala> :paste
:paste
// Entering paste mode (ctrl-D to finish)

object Test {
  def exec[F[_]: cats.Monad] = for {i: Int <- cats.data.EitherT.fromEither[F](Right(0))} yield i
}

^D
// Exiting paste mode, now interpreting.

<pastie>:12: error: value withFilter is not a member of cats.data.EitherT[F,E,Int]
  def exec[F[_]: cats.Monad] = for {i: Int <- cats.data.EitherT.fromEither[F](Right(0))} yield i

без :Int компилируется

Исправление этой ошибки состоит в том, чтобы просто явно не аннотировать левый аргумент <-. Таким образом, просто пропустите подсказку типа : Int как здесь:

scala> :paste
:paste
// Entering paste mode (ctrl-D to finish)

object Test {
  def exec[F[_]: cats.Monad] = for {i <- cats.data.EitherT.fromEither[F](Right(0))} yield i
}

^D
// Exiting paste mode, now interpreting.

warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
defined object Test

для справки

зависимости

"org.typelevel" %% "cats-core" % "1.1.0"
"org.typelevel" %% "cats-macros" % "1.1.0"
"org.typelevel" %% "cats-kernel" % "1.1.0"

sbt about

sbt:cats-eithert-problem> about
[info] This is sbt 1.1.4
[info] The current project is ProjectRef(uri("file:/Users/isaias/Projects/cats-eithert-problem/"), "root") 0.1.0-SNAPSHOT
[info] The current project is built against Scala 2.12.5
[info] Available Plugins: sbt.plugins.IvyPlugin, sbt.plugins.JvmPlugin, sbt.plugins.CorePlugin, sbt.plugins.JUnitXmlReportPlugin, sbt.plugins.Giter8TemplatePlugin, com.timushev.sbt.updates.UpdatesPlugin, spray.revolver.RevolverPlugin, com.typesafe.sbt.SbtNativePackager, com.typesafe.sbt.packager.archetypes.JavaAppPackaging, com.typesafe.sbt.packager.archetypes.JavaServerAppPackaging, com.typesafe.sbt.packager.archetypes.jar.ClasspathJarPlugin, com.typesafe.sbt.packager.archetypes.jar.LauncherJarPlugin, com.typesafe.sbt.packager.archetypes.scripts.AshScriptPlugin, com.typesafe.sbt.packager.archetypes.scripts.BashStartScriptPlugin, com.typesafe.sbt.packager.archetypes.scripts.BatStartScriptPlugin, com.typesafe.sbt.packager.archetypes.systemloader.SystemVPlugin, com.typesafe.sbt.packager.archetypes.systemloader.SystemdPlugin, com.typesafe.sbt.packager.archetypes.systemloader.SystemloaderPlugin, com.typesafe.sbt.packager.archetypes.systemloader.UpstartPlugin, com.typesafe.sbt.packager.debian.DebianDeployPlugin, com.typesafe.sbt.packager.debian.DebianPlugin, com.typesafe.sbt.packager.debian.JDebPackaging, com.typesafe.sbt.packager.docker.DockerPlugin, com.typesafe.sbt.packager.docker.DockerSpotifyClientPlugin, com.typesafe.sbt.packager.jdkpackager.JDKPackagerDeployPlugin, com.typesafe.sbt.packager.jdkpackager.JDKPackagerPlugin, com.typesafe.sbt.packager.linux.LinuxPlugin, com.typesafe.sbt.packager.rpm.RpmDeployPlugin, com.typesafe.sbt.packager.rpm.RpmPlugin, com.typesafe.sbt.packager.universal.UniversalDeployPlugin, com.typesafe.sbt.packager.universal.UniversalPlugin, com.typesafe.sbt.packager.windows.WindowsDeployPlugin, com.typesafe.sbt.packager.windows.WindowsPlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.12.4

sbt console

sbt:cats-eithert-problem> console
[info] Starting scala interpreter...
Welcome to Scala 2.12.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_144).
Type in expressions for evaluation. Or try :help.

Вопрос

Кто-нибудь знает, почему этот код зависит от withFilter только тогда, когда он явно аннотируется соответствующим типом или это ошибка?

Ответы [ 2 ]

0 голосов
/ 26 апреля 2018

Desugared код выглядит по-разному для обоих случаев.

sbt console с -Xprint:typer

После добавления scalacOptions ++= Seq("-Xprint:typer") в build.sbt можно было распечатать обессоленный код на sbt console:

без : Int

def exec         [F[_] >: [_]Nothing <: [_]Any](implicit evidence$1: cats.Monad[F]): cats.data.EitherT[F,Nothing,Int] = cats.data.EitherT.fromEither[F]
  .apply[Nothing, Int](scala.`package`.Right.apply[Nothing, Int](0))(evidence$1)
  .map[Int](((i: Int) => i))(evidence$1)

с : Int

def <exec: error>[F[_] >: [_]Nothing <: [_]Any](implicit evidence$1: cats.Monad[F]): <error>                          = cats.data.EitherT.fromEither[F]
  (Right(0)).<withFilter: error>((
    (<check$ifrefutable$1: error>: <error>) => 
      <check$ifrefutable$1: error>: @scala.unchecked match {
        case (<i: error> @ (_: Int)) => true
        case _ => false
      }
    )
  )
  .<map: error>(((i: Int) => i))

Однако я до сих пор не знаю, почему явный случай добавляет это withFilter.Кажется, что стратегия внутри компилятора состоит в том, чтобы НЕ доверять явным аннотациям типов, данным программистом.

, кажется, предназначен

Вот частьссылки , которую поделился со мной коллега, который показывает, что обычно все обрабатывается как шаблон на LHS генератора.

A generator p <- e </em> производитпривязки из выражения e , которое каким-то образом сопоставляется с шаблоном p

Теперь я почти уверен, что это не ошибка, хотяслучай, когда шаблон является просто идентификатором, а оптимизация не задокументирована в данной ссылке, это видно на меньшем примере:

без : Int

scala -Xprint:typer -e "for { i <- Option(0) } yield i"
[[syntax trees at end of                     typer]] // scalacmd2652417036573431085.scala
package <empty> {
  object Main extends scala.AnyRef {
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    };
    def main(args: Array[String]): Unit = {
      final class $anon extends scala.AnyRef {
        def <init>(): <$anon: AnyRef> = {
          $anon.super.<init>();
          ()
        };
        scala.Option.apply[Int](0).map[Int](((i: Int) => i))
      };
      {
        new $anon();
        ()
      }
    }
  }
}

без : Int

scala -Xprint:typer -e "for { i: Int <- Option(0) } yield i"
[[syntax trees at end of                     typer]] // scalacmd7960547396256503452.scala
package <empty> {
  object Main extends scala.AnyRef {
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    };
    def main(args: Array[String]): Unit = {
      final class $anon extends scala.AnyRef {
        def <init>(): <$anon: AnyRef> = {
          $anon.super.<init>();
          ()
        };
        scala.Option.apply[Int](0).withFilter(((check$ifrefutable$1: Int) => (check$ifrefutable$1: Int @unchecked) match {
  case (i @ (_: Int)) => true
  case _ => false
})).map[Int](((i: Int) => i))
      };
      {
        new $anon();
        ()
      }
    }
  }
}
0 голосов
/ 26 апреля 2018

для понимания составляется в комбинацию flatMap, map и withFilter. withFilter используется для предложения if для понимания, но даже когда нет if, withFilter все еще генерируется - см. объяснение в лучше-monadic-for

...