zygfryd / scala-zygf-cement

A factory macro for anonymous final static lazy variables. Also comes with implicit caching.

Version Matrix


This is a tiny macro library for Scala allowing minimum overhead caching of computation results, based on its position in source code. JDK 8’s invokedynamic instructions are generated under the hood to remember the result of the first invocation and return them on subsequent invocations.

In essence, cement is a factory for anonymous static lazy vals.

Supported Scala versions: 2.11*, 2.12, 2.13.

Alpha-quality software.


"net.zygfryd" %% "cement" % "0.2.1"
import zygf.cement.{cement, Cemented}

Cementing expressions

Performing a snippet of initialization code only once has never been this easy. Only the first invocation of this method will compile the pattern, while subsequent invocations return a cached value.

import java.util.regex._

def words(text: String): Array[String] = {
  val sep = cement { Pattern.compile("\\s+") }
Exercise caution as changing input variables will not cause the cemented expression to re-evaluate:
(1 to 3).map { i => cement(i) }.toList == List(1, 1, 1)

Cementing implicits

If you’re expecting to receive implicits that have a non-trivial cost to automatically generate, this library has your back:

def keepCalmAnd(implicit escapePlan: Cemented[EscapePlan]) = {
A separate Cemented[T] instance is created for each place of invocation, where an implicit Cemented[T] isn’t already provided.

Turning all implicit Cemented[T] into implicit T is one import away:

def bar(implicit ev: Evidence) = ???

def foo(implicit ev: Cemented[Evidence]) = {
  import Cemented.unwrap

To explicitly create an instance of Cemented[T]:

val cemented1: Cemented[_] = Cemented(expr) // performs cementing magic on expr
val cemented2: Cemented[_] = Cemented.wrap(expr) // performs no magic, simply allocates an instance

A more complete example and original motivation for creating this library is statically caching automatically created logger objects:

import org.apache.logging.log4j._
import zygf.cement.Cemented

object Logging
  implicit def makeAutoLogger(implicit scope: sourcecode.FullName): Logger = {

  def autoLogger(implicit logger: Cemented[Logger]) = logger.value

Two, not six, getLogger calls would have been made by the following code:

import Logging._

(1 to 3).foreach { _ =>


Retrieval of a previously cemented expression’s value incurs no allocations, dictionary lookups, exception handling or locking, it consists of:

  • one constant method handle call, possibly inlined

  • one branch on an instanceof check

  • one checked cast


Why is there an asterisk next to 2.11?

The Scala compiler had no invokedynamic support back then. For 2.11 we use a different approach, equally performant, but with a higher bytecode footprint. A new class is generated for every cemented call site, containing a static member and a couple methods.

What happens when an exception is thrown?

Exceptions aren’t remembered. The cemented computation will continue to get re-evaluated until it successfully returns a value.