Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.


Gitter - Columns and their card count Maven central

A Scala compiler plugin to give patterns and for-comprehensions the love they deserve

Note on Scala 3

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.

Getting started

The plugin is available on Maven Central.


addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")



Supports Scala 2.11, 2.12, and 2.13.1

Available plugin options

All options have form of -P:bm4:$feature:$flag

Feature Flag (default)
Desugaring without withFilter -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:

  • Disabling: n, no, 0, false
  • Enabling: y, yes, 1, true

Version Changes
0.3.1 Fix issues with wartremover, implicit patterns with = binds & Xplugin-list flag
0.3.0-M4 Fix anonymous variables in Scala 2.12.7+
M2, M3 Fixes for implicit patterns
0.3.0-M1 Initial implementation of implicit patterns
0.2.4 Fixed: incompatibility with Dsl.scala
0.2.3 Fixed: if-guards were broken when using untupling
0.2.2 Fixed: destructuring within for bindings (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 withFilters.


Desugaring for patterns without withFilters

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:

  .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:

  .map(_ match { case (x, y) => x + y })

Removing both withFilter and unchecked on generated map. So the code just works.

Additional Effects

Type ascriptions on LHS

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 String) => s"Count was $x")

This also works with flatMap and foreach, of course.

No silent truncation of data

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

Match warnings

Generators will now show exhaustivity warnings now whenever regular pattern matches would:

        import cats.syntax.option._

        for (Some(x) <- IO(none[Int])) yield x
: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]                      ^

Final map optimization

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 vals, saving on allocations and primitive boxing.

Define implicits in for-comprehensions or matches

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)


  • This plugin reserves one extra keyword, implicit0, if corresponding option for implicit patterns is enabled (which is by default).
  • Regular if guards are not affected, only generator arrows.