A compiler plugin to add a new phase to the compiler which supports mutual tail recursion.
Within your code add the mutualrec
annotation to methods which are mutually tail recursive:
import twotails.mutualrec
class Foo{
@mutualrec final def one(x: Int, y: Int): Int = if(0 < x) two(x-1, y+1) else y
@mutualrec final def two(x: Int, y: Int): Int = if(0 < x) one(y, x) else y
}
Some rules to follow to get the code to compile when working with this plugin:
val
, lazy val
, or var
. mutualrec
it will be replaced by a @tailrec
annotation.@tailrec
, methods annotated must be effectively final and the return type explicitly provided (effectively final being defined as final
, private
, part of a package object, or within a def
.) Twotails is currently published to Sonatype and the latest version is 0.3.1. To include this plugin for your project add the following two lines to your build file:
libraryDependencies ++= Seq(
compilerPlugin("com.github.wheaties" %% "twotails" % "0.3.1" cross CrossVersion.full),
"com.github.wheaties" %% "twotails-annotations" % "0.3.1" cross CrossVersion.full
)
TwoTails has been cross compiled and published against Scala 2.11.8. Tests are run against 2.11.7, 2.11.8 and 2.12.0 with the intention of supporting 2.12.x and Scala.js in the future. If you'd like 2.10 support, open a PR. Help is always appreciated. No guarantee exists
TwoTails does not handle method size limits. This leads to two problems:
TwoTails adds in a code transforming phase to the compiler. As such, code using macros or code enhancing techniques (such as that in Spring) may fail to compile at best or work in an unexpected manner at worst. We want to specifically call out AoP libraries such as AspectJ where the intention of an author is to have each "loop" of the recursive call trigger some side-effect. In such cases, similar to code using @tailcall
, only the first "loop" will induce the effect; all others will have been compiled away.
PRs are welcome and encouraged. Please do not leave any defamatory remarks or swear words in the comments of code submitted. TODOs may be left in so long as they are informative. Unit tests are nice but compilation tests nicer (a class
or object
stressing the plugin.) Purposefully failing constructs may be left in one of the unit test sections commented out until the project can be set up to run with Scala-Partest.
Bug reports are appreciated. Bug reports submitted with reproducible errors are more appreciated. Reports of issues with other compiler plugins should be submitted to all plugin projects. New features or feature requests are not bugs.
TwoTails supports the Typelevel Code of Conduct. All conversations within the Gitter room and/or other public online venues are asked to respect and adhere to the terms laid out therein (tl;dr? don't be rude, belittle or harass others.) Mutual recursion demands mutual respect.