为什么这段代码依赖于`withFilter`或者这是一个错误?

时间:2018-04-26 10:43:03

标签: scala scala-cats

我正在与项目中的猫一起工作,并且无法弄清楚为什么在这段代码中出现这种奇怪的编译器错误:

带有: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 :(得分:2)

理解的

被编译为flatMapmapwithFilter的组合。 withFilter用于for comprehension的if子句,但即使没有if,仍然会生成withFilter - 请参阅explanation at better-monadic-for

答案 1 :(得分:1)

对于这两种情况,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。编译器内部的策略似乎是 NOT 信任程序员给出的显式类型注释。

似乎是

Here is some piece of reference一位同事与我分享,表明通常所有事情都是作为发电机LHS的模式处理的。

  

生成器p&lt; - e 从表达式 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();
        ()
      }
    }
  }
}