ProductPromotion
Logo

Scala

made by https://0x3d.site

GitHub - ThoughtWorksInc/enableIf.scala: A library that toggles Scala code at compile-time, like #if in C/C++
A library that toggles Scala code at compile-time, like #if in C/C++ - ThoughtWorksInc/enableIf.scala
Visit Site

GitHub - ThoughtWorksInc/enableIf.scala: A library that toggles Scala code at compile-time, like #if in C/C++

GitHub - ThoughtWorksInc/enableIf.scala: A library that toggles Scala code at compile-time, like #if in C/C++

enableIf.scala

Join the chat at https://gitter.im/ThoughtWorksInc/enableIf.scala Build Status Latest version Scaladoc

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 call the flatMap method on TailRec. However, the method does not exist on Scala 2.10.

With the help of this library, You can create your own implementation of flatMap for Scala 2.10 target, and the Scala 2.11 target should still use the flatMap method implemented by Scala standard library.

Usage

Step 1: Add the library dependency in your build.sbt

// Enable macro annotation by scalac flags for Scala 2.13
scalacOptions ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Seq("-Ymacro-annotations")
  } else {
    Nil
  }
}

// Enable macro annotation by compiler plugins for Scala 2.12
libraryDependencies ++= {
  import Ordering.Implicits._
  if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) {
    Nil
  } else {
    Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full))
  }
}

libraryDependencies += "com.thoughtworks.enableIf" %% "enableif" % "latest.release"

Step 2: Create an implicit class for Scala 2.10

import com.thoughtworks.enableIf

@enableIf(scala.util.Properties.versionNumberString.startsWith("2.10."))
implicit class FlatMapForTailRec[A](underlying: TailRec[A]) {
  final def flatMap[B](f: A => TailRec[B]): TailRec[B] = {
    tailcall(f(underlying.result))
  }
}

The @enableIf annotation accepts a Boolean expression that indicates if the FlatMapForTailRec definition should be compiled. The Boolean expression is evaluated at compile-time instead of run-time.

Step 3: Call the flatMap method on your TailRec

import scala.util.control.TailCalls._
def ten = done(10)
def tenPlusOne = ten.flatMap(i => done(i + 1))
assert(tenPlusOne.result == 11)

For Scala 2.10, the expression scala.util.Properties.versionNumberString.startsWith("2.10.") is evaluated to true, hence the FlatMapForTailRec definition will be enabled. As a result, ten.flatMap will call to flatMap of the implicit class FlatMapForTailRec.

For Scala 2.11, the expression scala.util.Properties.versionNumberString.startsWith("2.10.") is evaluated to false, hence the FlatMapForTailRec definition will be disabled. As a result, ten.flatMap will call the native TailRec.flatmap.

Limitation

  • The enableIf annotation does not work for top level traits, classes and objects.
  • The boolean condition been evaluated must refer classs or objects via fully quantified names from dependency libraries
  • The boolean condition been evaluated must not refer other classs or objects 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 an ArrayBuffer for JVM target, and the native js.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$""")))
private object Jvm {
  
  def newBuffer[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$""")))
private object Js {

  @inline def newBuffer[A] = new scalajs.js.Array[A]

  @inline implicit final class ReduceToSizeOps[A] @inline()(array: scalajs.js.Array[A]) {
    @inline def reduceToSize(newSize: Int) = array.length = newSize
  }

}

import Js._
import Jvm._

val optimizedBuffer = newBuffer[Int]

optimizedBuffer += 1
optimizedBuffer += 2
optimizedBuffer += 3

// resolved to native ArrayBuffer.reduceToSize for JVM, implicitly converted to ReduceToSizeOps for Scala.js
optimizedBuffer.reduceToSize(1)

You can define a c parameter because the enableIf annotation accepts either a Boolean expression or a scala.reflect.macros.Context => Boolean function. You can extract information from the macro context c.

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:

object XYZ {
  @enableIf(classpathMatches(".*spark-catalyst_2\\.\\d+-3\\.2\\..*".r))
  private def getFuncName(f: UnresolvedFunction): String = {
    // For Spark 3.2.x
    f.nameParts.last
  }
  
  @enableIf(classpathMatches(".*spark-catalyst_2\\.\\d+-3\\.1\\..*".r))
  private def getFuncName(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 like ffmpeg-5.0-1.5.7-android-arm-gpl.jar, Please use classpathMactches or classpathContains.

Hints to show the full classpath:

sbt "show Compile / fullClasspath"

mill show foo.compileClasspath

More Resources
to explore the angular.

mail [email protected] to add your project or resources here 🔥.

Related Articles
to learn about angular.

FAQ's
to learn more about Angular JS.

mail [email protected] to add more queries here 🔍.

More Sites
to check out once you're finished browsing here.

0x3d
https://www.0x3d.site/
0x3d is designed for aggregating information.
NodeJS
https://nodejs.0x3d.site/
NodeJS Online Directory
Cross Platform
https://cross-platform.0x3d.site/
Cross Platform Online Directory
Open Source
https://open-source.0x3d.site/
Open Source Online Directory
Analytics
https://analytics.0x3d.site/
Analytics Online Directory
JavaScript
https://javascript.0x3d.site/
JavaScript Online Directory
GoLang
https://golang.0x3d.site/
GoLang Online Directory
Python
https://python.0x3d.site/
Python Online Directory
Swift
https://swift.0x3d.site/
Swift Online Directory
Rust
https://rust.0x3d.site/
Rust Online Directory
Scala
https://scala.0x3d.site/
Scala Online Directory
Ruby
https://ruby.0x3d.site/
Ruby Online Directory
Clojure
https://clojure.0x3d.site/
Clojure Online Directory
Elixir
https://elixir.0x3d.site/
Elixir Online Directory
Elm
https://elm.0x3d.site/
Elm Online Directory
Lua
https://lua.0x3d.site/
Lua Online Directory
C Programming
https://c-programming.0x3d.site/
C Programming Online Directory
C++ Programming
https://cpp-programming.0x3d.site/
C++ Programming Online Directory
R Programming
https://r-programming.0x3d.site/
R Programming Online Directory
Perl
https://perl.0x3d.site/
Perl Online Directory
Java
https://java.0x3d.site/
Java Online Directory
Kotlin
https://kotlin.0x3d.site/
Kotlin Online Directory
PHP
https://php.0x3d.site/
PHP Online Directory
React JS
https://react.0x3d.site/
React JS Online Directory
Angular
https://angular.0x3d.site/
Angular JS Online Directory