Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A library that toggles Scala code at compile-time, like #if in C/C++

License

NotificationsYou must be signed in to change notification settings

ThoughtWorksInc/enableIf.scala

Repository files navigation

Join the chat at https://gitter.im/ThoughtWorksInc/enableIf.scalaBuild StatusLatest versionScaladoc

enableIf.scala is a library that switches Scala code at compile-time, like#if in C/C++.

Motivation

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.

Usage

Step 1: Add the library dependency in yourbuild.sbt

// 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"

Step 2: Create an implicit class for Scala 2.10

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.

Step 3: Call theflatMap method on yourTailRec

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.

Limitation

  • TheenableIf annotation does not work for top level traits, classes and objects.
  • The boolean condition been evaluated must referclasss orobjects via fully quantified names from dependency libraries
  • The boolean condition been evaluated must not refer otherclasss orobjects from the same library.

Enable different code for Scala.js and JVM targets

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.

Enable different code for Apache Spark 3.1.x and 3.2.x

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:classpathMatchesArtifact is for classpath without classifiers. For classpath with classifiers likeffmpeg-5.0-1.5.7-android-arm-gpl.jar, Please useclasspathMactches orclasspathContains.

Hints to show the full classpath:

sbt"show Compile / fullClasspath"mill show foo.compileClasspath

About

A library that toggles Scala code at compile-time, like #if in C/C++

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors5

Languages


[8]ページ先頭

©2009-2025 Movatter.jp