-
Notifications
You must be signed in to change notification settings - Fork 136
Description
Proposal: Add Scala 3 Code Generation Support
Problem Statement
Guardrail currently generates Scala 2 code only. Users with Scala 3 projects cannot use guardrail-generated code without compatibility issues due to Scala 2-specific syntax (implicit parameter clauses, implicit values/definitions, package objects).
Proposed Solution
Add configuration option to generate Scala 3-compatible code while keeping guardrail's codebase on Scala 2.13.
Key Changes
-
Configuration: Add
--scala-versionCLI flag and plugin settings to specify target Scala version (2.12, 2.13, 3) -
Generated Code Transformations (when targeting Scala 3):
implicitparameter clauses →usingclausesimplicit valdefinitions →giveninstancesimplicit defdefinitions →giveninstances- Package objects → Regular objects (or top-level definitions)
- Maintain semantic equivalence with Scala 2 output
- Note: No implicit conversion (the language feature) is used in generated code
-
Scope: Update Scala framework generators (Akka HTTP, http4s, Scala Dropwizard)
- Java generators (Spring MVC, Java Dropwizard) generate Java code and are unaffected
- Pekko HTTP (external repo: guardrail-dev/guardrail-pekko-http) will need similar updates
Benefits
- Scala 3 projects can use guardrail-generated code
- No breaking changes to existing Scala 2 generation (feature flag approach)
- Most runtime dependencies Scala 3 compatible:
- ✅ cats 2.10.0 - full Scala 3 support
- ✅ circe 0.14.6 - full Scala 3 support
- ✅ http4s 0.23.24 - full Scala 3 support
⚠️ akka-http 10.2.10 - uses Scala 2.13 binaries viaCrossVersion.for3Use2_13(native Scala 3 support in 10.5.0+)⚠️ refined 0.11.0 - Scala 3 artifacts available but with limited macro functionality
- No architectural changes to guardrail internals required
Implementation
- Approach: Create syntax helper utilities, update ~7 key generator files (~250 update sites)
- Testing: Add Scala 3 to CI matrix, sample projects for each framework
- Compatibility: Maintain full backward compatibility with Scala 2 generation
Examples
CLI Usage
guardrail --scala-version 3 --specPath petstore.yaml --outputPath src/sbt Plugin
guardrailTasks := List(
ScalaServer(
file("petstore.yaml"),
pkg = "petstore",
framework = "http4s",
scalaVersion = "3"
)
)Generated Code Comparison
Scala 2:
implicit val encoder: Encoder[Pet] = deriveEncoder
def getPet(id: Long)(implicit ec: ExecutionContext): Future[Pet]Scala 3:
given Encoder[Pet] = deriveEncoder
def getPet(id: Long)(using ec: ExecutionContext): Future[Pet]Non-Goals
- Migrating guardrail's internal codebase to Scala 3 (future work)
- Using Scala 3-exclusive features (enums, union types) - can be added later
- Breaking changes to existing APIs
Open Questions
- Should Scala 3 generation be opt-in or should we auto-detect from project settings?
- What should be the default behavior if no version specified?
- Do we need version-specific OpenAPI extension (x-scala-version)?
Volunteering
We from Scala Teams are willing to contribute to implement this proposal if accepted by maintainers.
Next Steps
- Community feedback on approach
- Implement foundation (ScalaVersion config, utilities)
- Update generators systematically
- Add comprehensive testing
- Update documentation and examples
- Coordinate with build plugin repositories (sbt-guardrail, maven, gradle)
Technical Appendix: Implementation Scope
Actual Code Locations Requiring Changes
Based on codebase analysis, the following specific locations need updates:
Implicit Parameter Clauses (5 locations):
modules/scala-akka-http/src/main/scala/.../AkkaHttpClientGenerator.scala- Lines 511, 556modules/scala-akka-http/src/main/scala/.../AkkaHttpServerGenerator.scala- Lines 298, 456modules/scala-http4s/src/main/scala/.../Http4sClientGenerator.scala- Line 499
Change: Term.ParamClause(params, Some(Mod.Implicit())) → Scala3Compat.implicitsClause(params, scalaVersion)
Implicit Value/Definition Generation (~250 locations):
- CirceProtocolGenerator (~50 sites) - Encoder/Decoder instances
- CirceRefinedProtocolGenerator (~45 sites) - Refined type codecs
- JacksonProtocolGenerator (~110 sites) - Jackson serializers
- ScalaGenerator (~40 sites) - Type class instances, Show/Eq instances
- AkkaHttp/Http4s marshallers (~5 sites) - HTTP marshallers
Change: q"implicit val x: T = ..." → Scala3Compat.implicitVal(Some(name), tpe, rhs, scalaVersion)
Package Object Generation (1 location):
modules/scala-support/src/main/scala/.../ScalaGenerator.scala- Line 378
Change: Generate top-level definitions for Scala 3 instead of package object
Total: ~7 generator files, ~250 systematic replacements
Key Technical Facts
- No implicit conversions used - Only implicit parameters and implicit values
- Explicit codec generation - No auto-derivation, all codecs are hand-crafted
- No custom templates - Programmatic scala-meta AST generation only
- Single package object - Minimal migration surface area
- Mechanical transformation - All changes are systematic replacements
This makes the implementation low-risk and well-scoped.