Skip to content

Add Scala 3 Code Generation Support #2188

@Tanveer17

Description

@Tanveer17

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

  1. Configuration: Add --scala-version CLI flag and plugin settings to specify target Scala version (2.12, 2.13, 3)

  2. Generated Code Transformations (when targeting Scala 3):

    • implicit parameter clauses → using clauses
    • implicit val definitions → given instances
    • implicit def definitions → given instances
    • 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
  3. 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 via CrossVersion.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

  1. Should Scala 3 generation be opt-in or should we auto-detect from project settings?
  2. What should be the default behavior if no version specified?
  3. 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

  1. Community feedback on approach
  2. Implement foundation (ScalaVersion config, utilities)
  3. Update generators systematically
  4. Add comprehensive testing
  5. Update documentation and examples
  6. 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):

  1. modules/scala-akka-http/src/main/scala/.../AkkaHttpClientGenerator.scala - Lines 511, 556
  2. modules/scala-akka-http/src/main/scala/.../AkkaHttpServerGenerator.scala - Lines 298, 456
  3. modules/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):

  1. CirceProtocolGenerator (~50 sites) - Encoder/Decoder instances
  2. CirceRefinedProtocolGenerator (~45 sites) - Refined type codecs
  3. JacksonProtocolGenerator (~110 sites) - Jackson serializers
  4. ScalaGenerator (~40 sites) - Type class instances, Show/Eq instances
  5. 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

  1. No implicit conversions used - Only implicit parameters and implicit values
  2. Explicit codec generation - No auto-derivation, all codecs are hand-crafted
  3. No custom templates - Programmatic scala-meta AST generation only
  4. Single package object - Minimal migration surface area
  5. Mechanical transformation - All changes are systematic replacements

This makes the implementation low-risk and well-scoped.

Metadata

Metadata

Assignees

No one assigned

    Labels

    choreenhancementFunctionality that has never existed in guardrail

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions