koka-lang / koka

Koka language compiler and interpreter
http://koka-lang.org
Other
3.16k stars 151 forks source link

Versioned Identifiers #530

Open TimWhiting opened 1 month ago

TimWhiting commented 1 month ago

Daan and I at one point talked about extending the overloading to also handle versioning (which means that we would need to allow locally qualified type names as well).

In this way, users can mix and match versions and incrementally migrate their code, since both versions or multiple versions of the code would be available.

In other words we could have

type v1/mytype
type v2/mytype
fun v1/do-something(t: v1/mytype) ...

A user then would just keep using the API transparently, and when the package get's updated, if it typechecks at the newest type or function that is what it uses and otherwise it checks an older type. We could also allow using the newer functions and add implicit conversions to the newer type. In the package list file (I think yaml is great for that). We could allow upper bounds of versions which would treat the newest version for that package as the specified version. Though a user can always use the locally qualified version to ensure they always get what they expect. I think it would be good to have a convention if not requirement that the version identifier is outside all other local qualifiers or in the module path. Additionally I would like the user to be able to specify the version without requiring additional local qualifiers.

There is a lot to discuss and think about with this, to determine whether this is a good idea.

The idea then is that package publishing versioning and api versioning are treated separately. A package version once published will never change, and might not have anything to do with api versioning, though we might want to enforce some conventions. However, in each published version the apis are versioned at a much more granular level, and a single package release should ideally contain at least the last two versions to allow migration time (maybe more or all of them?), and interoperability between your dependency on the package and a transitive dependency on the package, or two transitive dependencies on the package. An api without a version number assumes that it is the most recent. Dependencies are resolved according to their package list and their internal code is expanded to fully qualified identifiers prior to typechecking your module.

In particular it would be great to have package publishing tooling highlight or error on changes to apis that should really be marked with a new version - including changes to the function signature, or removed fields in types. Changes to the function implementation / it's dependencies could also be flagged and approved if they are just bug fixes. Additionally it would be nice to have some diff tooling that expands the inferred fully qualified identifiers in a diff between two package versions.

We could also use implicits or automatically insert implicits to do coercion of datatypes to the newest api if the package author provides a coercion for the type.

As far as tooling, I think we would want to explicitly highlight versions in a different color than the rest of qualfiiers, and add configuration to insert inlay hints for versions separately from showing fully qualified names or just locally qualified names, so you get the right kind of noise. The default might be to show inferred versions.

Anyways this could involve a lot of tooling changes and infrastructure, but it important to figure out prior to building out too much of a packaging ecosystem.