- Notifications
You must be signed in to change notification settings - Fork4
A library that toggles Scala code at compile-time, like #if in C/C++
License
ThoughtWorksInc/enableIf.scala
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
enableIf.scala is a library that switches Scala code at compile-time, like#if in C/C++.
Suppose you want to create a library for both Scala 2.10 and Scala 2.11. When you implement the library, you may want to calltheflatMap method onTailRec. However, the method does not exist on Scala 2.10.
With the help of this library, You can create your own implementation offlatMap for Scala 2.10 target, and the Scala 2.11 target should still use theflatMap method implemented by Scala standard library.
// Enable macro annotation by scalac flags for Scala 2.13scalacOptions++= {importOrdering.Implicits._if (VersionNumber(scalaVersion.value).numbers>=Seq(2L,13L)) {Seq("-Ymacro-annotations") }else {Nil }}// Enable macro annotation by compiler plugins for Scala 2.12libraryDependencies++= {importOrdering.Implicits._if (VersionNumber(scalaVersion.value).numbers>=Seq(2L,13L)) {Nil }else {Seq(compilerPlugin("org.scalamacros"%"paradise"%"2.1.1" crossCrossVersion.full)) }}libraryDependencies+="com.thoughtworks.enableIf"%%"enableif"%"latest.release"
importcom.thoughtworks.enableIf@enableIf(scala.util.Properties.versionNumberString.startsWith("2.10."))implicitclassFlatMapForTailRec[A](underlying:TailRec[A]) {finaldefflatMap[B](f:A=>TailRec[B]):TailRec[B]= { tailcall(f(underlying.result)) }}
The@enableIf annotation accepts aBoolean expression that indicates if theFlatMapForTailRec definition should be compiled. TheBoolean expression is evaluated at compile-time instead of run-time.
importscala.util.control.TailCalls._deften= done(10)deftenPlusOne= ten.flatMap(i=> done(i+1))assert(tenPlusOne.result==11)
For Scala 2.10, the expressionscala.util.Properties.versionNumberString.startsWith("2.10.") is evaluated totrue, hence theFlatMapForTailRec definition will be enabled. As a result,ten.flatMap will call toflatMap of the implicit classFlatMapForTailRec.
For Scala 2.11, the expressionscala.util.Properties.versionNumberString.startsWith("2.10.") is evaluated tofalse, hence theFlatMapForTailRec definition will be disabled. As a result,ten.flatMap will call the nativeTailRec.flatmap.
- The
enableIfannotation does not work for top level traits, classes and objects. - The boolean condition been evaluated must refer
classs orobjects via fully quantified names from dependency libraries - The boolean condition been evaluated must not refer other
classs orobjects from the same library.
Suppose you want to create a Buffer-like collection, you may want create anArrayBuffer for JVM target, and the nativejs.Array for Scala.js target.
/** * Enable members in `Jvm` if no Scala.js plugin is found (i.e. Normal JVM target)*/@enableMembersIf(c=>!c.compilerSettings.exists(_.matches("""^-Xplugin:.*scalajs-compiler_[0-9\.\-]*\.jar$""")))privateobjectJvm {defnewBuffer[A]= collection.mutable.ArrayBuffer.empty[A] }/** * Enable members in `Js` if a Scala.js plugin is found (i.e. Scala.js target)*/@enableMembersIf(c=> c.compilerSettings.exists(_.matches("""^-Xplugin:.*scalajs-compiler_[0-9\.\-]*\.jar$""")))privateobjectJs {@inlinedefnewBuffer[A]=new scalajs.js.Array[A]@inlineimplicitfinalclassReduceToSizeOps[A]@inline()(array: scalajs.js.Array[A]) {@inlinedefreduceToSize(newSize:Int)= array.length= newSize }}importJs._importJvm._valoptimizedBuffer= newBuffer[Int]optimizedBuffer+=1optimizedBuffer+=2optimizedBuffer+=3// resolved to native ArrayBuffer.reduceToSize for JVM, implicitly converted to ReduceToSizeOps for Scala.jsoptimizedBuffer.reduceToSize(1)
You can define ac parameter because theenableIf annotation accepts either aBoolean expression or ascala.reflect.macros.Context => Boolean function. You can extract information from the macro contextc.
For breaking API changes of 3rd-party libraries, simply annotate the target method with the artifactId and the version to make it compatible.
To distinguish Apache Spark 3.1.x and 3.2.x:
objectXYZ {@enableIf(classpathMatches(".*spark-catalyst_2\\.\\d+-3\\.2\\..*".r))privatedefgetFuncName(f:UnresolvedFunction):String= {// For Spark 3.2.x f.nameParts.last }@enableIf(classpathMatches(".*spark-catalyst_2\\.\\d+-3\\.1\\..*".r))privatedefgetFuncName(f:UnresolvedFunction):String= {// For Spark 3.1.x f.name.funcName }}
For specific Apache Spark versions:
@enableIf(classpathMatchesArtifact(crossScalaBinaryVersion("spark-catalyst"),"3.2.1"))@enableIf(classpathMatchesArtifact(crossScalaBinaryVersion("spark-catalyst"),"3.1.2"))
NOTICE:
classpathMatchesArtifactis for classpath without classifiers. For classpath with classifiers likeffmpeg-5.0-1.5.7-android-arm-gpl.jar, Please useclasspathMactchesorclasspathContains.
Hints to show the full classpath:
sbt"show Compile / fullClasspath"mill show foo.compileClasspathAbout
A library that toggles Scala code at compile-time, like #if in C/C++
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors5
Uh oh!
There was an error while loading.Please reload this page.
