Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bincompat-friendly codegen #1500

Open
kubukoz opened this issue Apr 22, 2024 · 1 comment
Open

Bincompat-friendly codegen #1500

kubukoz opened this issue Apr 22, 2024 · 1 comment
Assignees
Labels
question Further information is requested
Milestone

Comments

@kubukoz
Copy link
Member

kubukoz commented Apr 22, 2024

Related: #1485, smithy-lang/smithy#2243, Discord discussion

In some situations, such as generating protocol shapes (not limited to, but in particular: traits), it would be beneficial to allow evolving models without breaking binary compatibility of generated code.

For example, adding a new field to a struct trait shouldn't break binary compatibility of the result.

We should come up with and agree on:

  • conditions under which we would generate a shape as bincompat-friendly
    • for example: all traits? per-namespace/per-shape configuration? Something like what we're doing for validated newtypes in Validated newtypes #1454?
  • an exact encoding that'll work across all supported Scala versions. Some starting points for discussion:
    • non-case classes
    • public getters
    • public withX (for a member named x) instead of copy methods
    • mutable builders?

and implement these for the next minor version (0.19), if possible.

@kubukoz kubukoz added the question Further information is requested label Apr 22, 2024
@kubukoz kubukoz added this to the 0.19.0 milestone Apr 22, 2024
@kubukoz kubukoz self-assigned this Aug 30, 2024
@kubukoz
Copy link
Member Author

kubukoz commented Sep 2, 2024

Made some progress with @Baccata.

I won't write down the exact encoding just yet (I changed my mind several times in the last couple hours), but it appears that we'll draw a lot of inspiration from Contraband, including a @since("1.0") trait (or similar) to allow marking new members as having been added in a particular version.

If there's a bunch of versions, we can generate a constructor / apply for each of them, e.g.

//assume everything is required

structure Foo {
  s1: String
  @since("1.0") s2: String = "test2"
  @since("1.0") s3: String = "test3"
  s4: String
}

// we can add these traits for external shapes too, with `apply`
apply Foo$s4 @since("2.0")
apply Foo$s4 @default("test4")
final case class Foo private(s1: String, s2: String, s3: String, s4: String) { ... }
object Foo {
  def apply(s1: String): Foo = new Foo(s1, "test2", "test3", "test4")
  def apply(s1: String, s2: String, s3: String): Foo = new Foo(s1, s2, s3, "test4")
  def apply(s1: String, s2: String, s3: String, s4: String) = new Foo(s1, s2, s3, s4)
}

I'll play with Contraband soon to see if it does this (and other interesting cases it handles, such as adding new values between existing ones) and get back with an update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant