Our proposal is to add Using Directives, a key-value pair-based syntax for delivering Scala program configurations. Here’s an example:
using scala "3.1.2"
@main def hello = println(“Hello from Scala 3.1.2”)
Using directives can be placed on top of the .scala file, above imports, package definition and everything else that is not a comment. This proposal does not imply that the Scala compiler would need to resolve dependencies or apply specific options; the role of using directives is to provide a configuration for build tools or IDEs. From the compiler’s point of view, using directives will be treated just like a comment with a fixed syntax.
This proposal does not cover the semantics of the using directives (e.g. their scope or how to resolve conflicts if directives come from multiple files). If such semantics should be a part of the language specification, then we suggest adopting semantics from Scala CLI.
Standardising a set of supported keys across different tools is not a core part of this proposal, yet we believe it would be beneficial. The list of directives used by Scala CLI would be a good starting point for a standardised list of settings.
Motivation
The primary motivation for introducing using directives is the ability to keep configuration together with code and share it as a simple text rather than a project with a structure. In addition, scripts, bug reports, tutorials, education materials, REPL or notebooks would greatly benefit from the ability to include configuration.
Magic imports from Ammonite are a good illustration that in-source configuration is needed in certain use cases.
Additionally, using directives could provide universal syntax shared across build tools, scripts, REPL or code snippets.
Using directives is a core part of Scala CLI; however, at this moment, we recommend placing them in comments using a special syntax: “//> using scala 3.1” and that’s a bit counterintuitive and misleading.
Using directives syntax:
We propose the following syntax for using directives:
UsingDirective ::= "using" (Setting | Settings)
Settings ::= "{" Setting { ";" Setting } [";"] "}"
Setting ::= Ident [Values | Settings]
Ident ::= ScalaIdent { “.” ScalaIdent }
Values ::= Value { "," Value} [","]
Value ::= stringLiteral | ["-"] numericLiteral | true | false
Where:
- A
Settings
block is similar to the standard Scala code block. Braces and indentation syntax is allowed. e.g.:
someSettings { setting1 value 1; setting2; }
// or
someSettings {
setting1
setting2
}
// or
someSettings:
setting1
setting2
-
Ident
is the standard Scala identifier or list of identifiers separated by dots:
foo
foo.bar
`foo-bar`.bazz
- String literals and numeric literals are similar to Scala.
- No value after the identifier is treated as
true
value:using scalaSettings.fatalWarnings
- Specifying a setting with the same path more than once and specifying the same setting with a list of values are equivalent
- A path created using dot-separated idents is semantically equivalent to a path created by nesting idents:
foo.bar
is equivalent tofoo { bar }
Implementation
Scala CLI employs using directives to create a persistent configuration for projects. However, users still can deliver configuration data in the command line, which overrides any values from using directives.
Under the hood, Scala CLI uses a dedicated library called using_directives for parsing. Then it processes the data provided to extract the build configuration values. The library is written in Java, so it can be used in build tools / IDEs regardless of the Scala version. Furthermore, as the library is basically a fragment of the Scala 3 parser rewritten to Java, we believe that adding support for using directives to Scala compilers shouldn’t be difficult.
The library is still under development.