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
Available plugin optionsAll options have form of -P:bm4:$feature:$flag
-P:bm4:no-filtering:y
Elimination of identity map -P:bm4:no-map-id:y
Elimination of tuples in bindings -P:bm4:no-tupling:y
Implicit definining patterns -P:bm4:implicit-patterns:y
Supported values for flags:
n
, no
, 0
, false
y
, yes
, 1
, true
(bar, baz) = foo
0.2.1 Fixed: untupling with -Ywarn-unused:locals
causing warnings on e.g. _ = println()
. 0.2.0 Added optimizations: map elimination & untupling. Added plugin options. 0.1.0 Initial version featuring for desugaring without withFilter
s. Desugaring for
patterns without withFilter
s Destructuring Either
/ 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.
Type ascriptions on left-hand side do not become an isInstanceOf
check - which they do by default. E.g.
def getThing: IO[String] = ??? for { x: String <- getCounts } yield s"Count was $x"
would desugar directly to
getCounts.map((x: String) => s"Count was $x")
This also works with flatMap
and foreach
, of course.
This example is taken from Scala warts post by @lihaoyi
// Truncates 5 for((a, b) <- Seq(1 -> 2, 3 -> 4, 5)) yield a + " " + b // Throws MatchError Seq(1 -> 2, 3 -> 4, 5).map{case (a, b) => a + " " + b}
With the plugin, both versions are equivalent and result in MatchError
Generators will now show exhaustivity warnings now whenever regular pattern matches would:
import cats.syntax.option._ for (Some(x) <- IO(none[Int])) yield x
D:\Code\better-monadic-for\src\test\scala\com\olegpy\TestFor.scala:66
:22: match may not be exhaustive.
[warn] It would fail on the following input: None
[warn] for (Some(x) <- IO(none[Int])) yield x
[warn] ^
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))Desugar bindings as vals instead of tuples
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
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4