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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(go) - research using generics for improved UX #3276

Closed
2 of 6 tasks
MrArnoldPalmer opened this issue Dec 21, 2021 · 4 comments
Closed
2 of 6 tasks

feat(go) - research using generics for improved UX #3276

MrArnoldPalmer opened this issue Dec 21, 2021 · 4 comments
Assignees
Labels
feature-request A feature should be added or improved. p1

Comments

@MrArnoldPalmer
Copy link
Contributor

MrArnoldPalmer commented Dec 21, 2021

馃殌 Feature Request

Affected Languages

  • TypeScript or Javascript
  • Python
  • Java
  • .NET (C#, F#, ...)
  • Go

General Information

  • JSII Version: 1.49.0

  • Platform: *

  • This feature might incur a breaking change

Description

Go 1.18 is scheduled to release as stable in Feb 2022 with support for generic types. This new feature allows us to potentially take an alternative approach to supporting optional values, enabling type casting, and various runtime improvements.

Proposed Solution

Optional Types

Definition of some wrapper type like:

type Optional[T any] struct{
	val T
	defined bool
}

Would allow code generating Optional[Type] instead of *Type everywhere. This largely would have similar semantics to the user as all types, regardless of whether they are optional or required in typescript, would have to be this wrapper type in order to uphold backwards compatibility guarantees as detailed in the RFC. It may however emerge as the more idiomatic solution once generics are commonly used in Go, and they can be defined inline instead of using a utility function like jsii.String or needing to allocate a variable previously.

Additionally this could simplify reflection code within the Go runtime where currently some relatively complicated logic exists around inspecting and dereferencing pointers during reflection.

Type Casting

We may be able to define a function, returning a generic type representing a parent or child class (in typescript) to support downcasting as needed for escape hatches.

Runtime Improvements

Changing JSII runtime functionality to use generics:

// from
func (c *Client) Get(props GetProps) (response GetResponse, err error)
// to
func (c *Client) Get[A any](props GetProps) (response GetResponse, err error, val A)

Could potentially give stronger compile time guarantees for the runtime library and generated code and reduce the usage of unsafe pointer manipulation.

@MrArnoldPalmer MrArnoldPalmer added feature-request A feature should be added or improved. p1 labels Dec 21, 2021
@MrArnoldPalmer MrArnoldPalmer changed the title feat(go) - research replacing optional types with wrapper type using generics feat(go) - research using generics for improved UX Dec 21, 2021
@RomainMuller RomainMuller self-assigned this Dec 21, 2021
@RomainMuller
Copy link
Contributor

RomainMuller commented Dec 23, 2021

I extensively prototyped in the https://github.com/aws/jsii/tree/rmuller/explore/go1.18-generics branch.

Here are some of the high-level findings (pending a more comprehensive writeup):

  • Methods cannot introduce new type arguments (they may of course use the type parameters of the type they are a member of)
  • We can design a "marker" interface that can be used in any place where a value is optional (nil-able)
    type Option[T any] interface {
      // This is merely a marker function to facilitate
      // getting the underlying value type through reflection
      Unwrap__() T
    }
    • All generated types simply implement this interface (T is the implementing type)
      • This means a signature that accepts any T can be updated to accept Optional[T] without breaking
      • Unfortunately any given type can only implement the marker ONCE (as it'd otherwise require multiple identically-named methods to be implemented, which is impossible)
    • Structs (in the jsii sense) and enums are passed by-value when required, so they simply cannot be nil.
    • We still need to use wrappers for primitive types (string, float64, etc...), but instead of being pointers to these types, they can be "real" aliases
      type String string
      // String is a valid value for Option[String]
      func (s String) Unwrap__() String { return s }
      • These cannot be nil when required.
      • They implement Option[T]
      • These types have perhaps a more "go native" feel to them.
    • The marker allows runtime validation that structs have values for all required (and nil-able) fields they have
    • Pointers are no longer used anywhere except for method's receiver types, which means the kernel's code can do away with all the pointer indirection logic, and only deal with interfaces instead.
  • We can streamline the runtime APIs that currently accept reflect.Type so they are instead generic functions
    // Before
    RegisterStruct(reflect.TypeOf((*StructType)(nil).Elem())
    
    // After:
    RegisterStruct[StructType]()
  • We can implement a decent UnsafeCast feature using generics, where the experience could look like so:
    original := SomeFunction() // A union of structs
    downCasted := jsii.UnsafeCast[SpecificStruct](original)

@MrArnoldPalmer
Copy link
Contributor Author

These definitely look like some significant improvements that at first glance appear to make the Go experience easier to get started with and understand. We should work towards providing a working experience for users to try and provide feedback on though if possible without rewriting too much of our current implementation, if only to provide further validation without committing to an entirely new approach.

@RomainMuller
Copy link
Contributor

Additional notes in aws/aws-cdk-rfcs#397

@github-actions
Copy link
Contributor

鈿狅笍COMMENT VISIBILITY WARNING鈿狅笍

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request A feature should be added or improved. p1
Projects
Status: Done
Development

No branches or pull requests

2 participants