A Scala compiler plugin to give patterns and for-comprehensions the love they deserve
Scala 3.0.0 natively supports the semantic changes provided by better-monadic-for under -source:future
compiler flag. The following code is considered valid under this flag:
for {
(x, given String) <- IO(42 -> "foo")
} yield s"$x${summon[String]}"
There are no changes to map
desugaring and value bindings inside fors still allocate tuples to my current knowledge. I don't currently have plans on rewriting plugin for Scala 3, however.
See changes: pattern bindings and contextual abstractions: pattern-bound given instances.
The plugin is available on Maven Central.
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<configuration>
<compilerPlugins>
<compilerPlugin>
<groupId>com.olegpy</groupId>
<artifactId>better-monadic-for_2.13</artifactId>
<version>0.3.1</version>
</compilerPlugin>
</compilerPlugins>
</configuration>
</plugin>
Supports Scala 2.11, 2.12, and 2.13.1
for
patterns without withFilter
sEither
/ IO
/ Task
/ FlatMap[F]
This plugin lets you do:
import cats.implicits._
import cats.effect.IO
def getCounts: IO[(Int, Int)] = ???
for {
(x, y) <- getCounts
} yield x + y
With regular Scala, this desugars to:
getCounts
.withFilter((@unchecked _) match {
case (x, y) => true
case _ => false
}
.map((@unchecked _) match {
case (x, y) => x + y
}
Which fails to compile, because IO
does not define withFilter
This plugin changes it to:
getCounts
.map(_ match { case (x, y) => x + y })
Removing both withFilter
and unchecked
on generated map
. So the code just works.
Eliminate calls to .map
in comprehensions like this:
for {
x <- xs
y <- getYs(x)
} yield y
Standard desugaring is
xs.flatMap(x => getYs(x).map(y => y))
This plugin simplifies it to
xs.flatMap(x => getYs(x))
Direct fix for lampepfl/dotty#2573.
If the binding is not used in follow-up withFilter
, it is desugared as
plain val
s, saving on allocations and primitive boxing.
Since version 0.3.0-M1, it is possible to define implicit values inside for-comprehensions using a new keyword implicit0
:
case class ImplicitTest(id: String)
for {
x <- Option(42)
implicit0(it: ImplicitTest) <- Option(ImplicitTest("eggs"))
_ <- Option("dummy")
_ = "dummy"
_ = assert(implicitly[ImplicitTest] eq it)
} yield "ok"
In current version (0.3.0) it's required to specify a type annotation in a pattern with implicit0
.
It also works in regular match clauses:
(1, "foo", ImplicitTest("eggs")) match {
case (_, "foo", implicit0(it: ImplicitTest)) => assert(implicitly[ImplicitTest] eq it)
}
implicit0
, if corresponding option for implicit patterns is enabled (which is by default).if
guards are not affected, only generator arrows.MIT