question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Allow customisation of metaprogramming via scalac flags

See original GitHub issue

This issue is to allow inline code to customise its output based on user-defined settings provided as scalac flags.

Background

Full detail here: https://contributors.scala-lang.org/t/metaprogramming-configurability/4961

This issue is the first part of the solution I proposed above.

Proposal

Disclaimer: the names used below are just drafts and still need some good bikeshedding.

  • Add a new scalac flag that takes a -E:key=value (similar to how java accepts -Dkey=value args)
  • Add transparent inline def envGet(inline key: String): Option[String] to scala.compiletime that provides access to the settings specified with above scalac flags

Usage Example

The very first example in the inlining doc is a logger that can be configured at compile-time, and is “zero-cost” (as they say) at runtime.

  • when logging = true then calls to log() generate println statements
  • when logging = false then calls to log() generate nothing
  • there is never a logging-config check at runtime

Currently, the problems with this are

  1. Logger and its Config are static, and changing the setting is a code change
  2. Config must be pre-configured and defined alongside Logger (else Logger wouldn’t compile)
  3. If Logger were a library, downstream users would have no way of configuring it

If this issue were implemented, our zero-cost-ish Logger could be written like this below, to accept a "myLogger.level" setting that downstream users can populate:

import scala.compiletime.*

object Logging {
  private inline val Trace = 0
  private inline val Debug = 1
  private inline val Info  = 2
  private inline val Warn  = 3

  private transparent inline def chosenThreshold: Int =
    inline envGet("myLogger.level") match
      case Some("TRACE") => Trace
      case Some("DEBUG") => Debug
      case Some("INFO")  => Info
      case Some("WARN")  => Warn
      case None          => Warn // let's provide a default out-of-the-box
      case Some(x)       => error("Unsupported logging level: " + x)

  private inline def log(inline lvl: Int, inline msg: String): Unit =
    inline if lvl >= chosenThreshold then println(msg) else ()

  // This is the public API
  inline def trace(inline msg: String) = log(Trace, msg)
  inline def debug(inline msg: String) = log(Debug, msg)
  inline def info (inline msg: String) = log(Info , msg)
  inline def warn (inline msg: String) = log(Warn , msg)
}

And then a downstream user could specify -E:myLogger.level=INFO in their scalac flags so that our toy logging library effectively becomes this for them:

object Logging {
  inline def trace(inline msg: String) = ()
  inline def debug(inline msg: String) = ()
  inline def info (inline msg: String) = println(msg)
  inline def warn (inline msg: String) = println(msg)
}

Reconsidering the three problems above, with this new solution:

  1. Logger can now be configured dynamically, and via config (no code changes required)
  2. Config is no longer necessary. Whether the library author wants to provide it and/or defaults is now something under their control.
  3. Downstream users can now configure the behaviour of Logger

PR?

I’m not asking that the busy Scala 3 team take some time out to implement this. This seemed simple enough for me to try implementing it myself and so I have, and it seems to work well! The above logging example is one of the tests and is confirmed to work as excepted.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:4
  • Comments:19 (18 by maintainers)

github_iconTop GitHub Comments

1reaction
neko-kaicommented, Nov 9, 2021

@Baccata I think you can retrieve sourcepath using

scala.quoted.quotes.reflect.Position.ofMacroExpansion.sourceFile.getJPath

This retrieves the absolute path to the current source file. I thihk the macro below solves a similar problem to yours - https://github.com/7mind/sbtgen/pull/181/commits/e6f96a4cf28b002432be79bdf0a27f8d3c528ea1#diff-1fa20673ba6904c5702fdceacd1fa15adf52b5e86dc66b4ccce8efebe3586b94R73

1reaction
nicolasstuckicommented, Apr 9, 2021

⚠️ Note that this is an extremely dangerous feature as it will introduce instability in the generated artifacts long term. Once we allow recompilation from TASTy, having a different set of flags could change the behavior of the already distributed artifact. Each user might see different behaviors depending on their setup. Additionally, this change of behavior would be different in transparent and non-transparent inlining. ⚠️

Read more comments on GitHub >

github_iconTop Results From Across the Web

Scala Compiler Options
The Scala compiler scalac offers various compiler options, or flags, that change the compiler's default behavior. Some options just generate more compiler ...
Read more >
Unification of Compile-Time and Runtime Metaprogramming ...
We describe the architecture that allows the imple- mentation of the framework to reuse the existing compiler codebase and validate this ...
Read more >
Expressive Metaprogramming for Scala 3 - gists · GitHub
Desired Metaprogramming Capabilities · adding new declarations (fields, methods, types, classes, etc) at compile-time · into existing named traits/classes/objects ...
Read more >
Method Override with Scala 3 Macros - Stack Overflow
My questions are: Is it possible at all to derive custom types without explicitly invoking: new Class {} in quotes? Does ClassDef.copy ...
Read more >
harnessing Scala 3 metaprogramming without macros - Scalac
The answer is really simple – the type system was not powerful enough* (there were some other ways to do that) and the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found