Skip to content

Instantly share code, notes, and snippets.

@da-tubi
Last active April 27, 2022 06:34
Show Gist options
  • Save da-tubi/5f9ee2218fe1714ab984468139d5c240 to your computer and use it in GitHub Desktop.
Save da-tubi/5f9ee2218fe1714ab984468139d5c240 to your computer and use it in GitHub Desktop.

Scala Paradise Case Studies


Macro Paradise Plugin

https://docs.scala-lang.org/overviews/macros/paradise.html

  • 2.10.x
  • 2.11.x
  • 2.12.x
  • 2.13.x (built-in in scalac)

Macro demo in other programming languages

#define sum(X, Y) X+Y
@Data public class DataExample {
  private final String name;
}

Scala Macro (Opinionated)

  • A lie of less code which can do more
  • Macro should not be created in real world
    • Most general purpose and useful macros has been created in the open source community
    • Hard to maintain and hard to debug
    • Painful migration for Scala 2 to Scala 3

Scala Macros


Case Study: Logging

logger.debug(s"Some $expensive message!") 
if (logger.isDebugEnabled) logger.debug(s"Some $expensive message!") 
final class Logger private (val underlying: org.slf4j.Logger) {
  def debug(message: String): Unit = macro LoggerMacro.debugMessage
}

IMPL: Scala String -> AST

message.tree match {
  case q"scala.StringContext.apply(..$parts).s(..$args)" =>
    val format = parts.iterator.map({ case Literal(Constant(str: String)) => str })
      // Emulate standard interpolator escaping
      .map(StringContext.processEscapes)
      // Escape literal slf4j format anchors if the resulting call will require a format string
      .map(str => if (args.nonEmpty) str.replace("{}", "\\{}") else str)
      .mkString("{}")

    val formatArgs = args.map(t => c.Expr[Any](t))

    (c.Expr(q"$format"), formatArgs)

  case _ => (message, Seq.empty)
}

Case Study: lombok.Data

@data
class A {
  var x: Int = _
  var y: String = _
}

val a = new A
a.setX(12)
assert(a.getX === 12)
a.setY("Hello")
assert(a.getY === "Hello")

In Summary

We now have two demo macro project for:

  • interpolate the AST of a scala method
  • interpolate the AST of a scala class

Application: config-annotation

@conf trait kafka {
  val server = new {
    val host = "wacai.com"
    val port = 12306
  }

  val socket = new {
    val timeout = 3 seconds
    val buffer = 1024 * 64L
  }

  val client = "wacai"
}

Application: config-annotation

trait kafka {
  val server = new {
    val host = config.getString("kafka.server.host")
    val port = config.getInt("kafka.server.port")
  }
  val socket = new {
    val timeout = Duration(config.getDuration("kafka.socket.timeout",
      SECONDS))
    val buffer = config.getBytes("kafka.socket.buffer")
  }
  val client = config.getString("kafka.client")

  ...
}

Application: conditional compile

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
  }
}

Thank you!!!

https://github.com/darcy-shen/paradise-study

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment