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

RFC 001: Extend Quint type system to support Sum Types #1062

Merged
merged 24 commits into from
Aug 21, 2023

Conversation

shonfeder
Copy link
Contributor

@shonfeder shonfeder commented Jul 19, 2023

This RFC proposes the design for sum types to be introduced to quint.

The overview gives the gist of the proposal and detailed discussion follows.

Closes #673.

  • Tests added for any new code
  • Documentation added for any new functionality
  • Entries added to the respective CHANGELOG.md for any new functionality
  • Feature table on README.md updated for any listed functionality

doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
Copy link
Contributor

@konnov konnov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very detailed RFC. Thank you for thinking about the sum types. I reviewed this document with the VSCode plugin, so this may have lead to too many comments. If it's too hard to read on Github, try the plugin.

doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
Comment on lines 581 to 584
In the presence of row-polymorphis, however, the responsibility of the recipient
is relaxed: the recipient can just handle a subset of the possible alternatives,
and if the expression falls under a label they are not prepared to handle, they
can pass the remaining responsibility on to another handler.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This explanation makes sense to me. However, I am not sure how we would handle that in Apalache. Maybe it is not a problem at all, since Apalache variants would require us to write series of IF-THEN-ELSE with unsafe access, but we should check that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can translate this into Apalache IR using the VariantGetUnsafe and VariantTag operators, like the pattern here:

https://github.com/informalsystems/apalache/blob/4e0094ee5d03a068188a85dcdc8ead021b17033e/src/tla/Option.tla#L34-L60

doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
doc/rfc001-sum-types.md Outdated Show resolved Hide resolved
@shonfeder
Copy link
Contributor Author

shonfeder commented Jul 21, 2023

I still need to do a pretty extensive editing, clean up, and revision pass, but I've made the following improvements following our conversation:

  • Committed a generated pdf, so the latex is easily and reliably readable. Feedback as annotated PDF is welcome!
  • Made the syntax more rust-like in the ways we discussed (swapped | for ,)
  • Added a fragment from the ERC20 spec that has been adapted to use sum types, so we can get a feel for what this syntax will look like in a spec.
  • Changed the injection rule so that we ensure a (necessarily closed) sum type has been defined.
  • Some cleanup to the first part of the doc.

Sorry for the rough state of the rest, but I look forward to any thoughts in these changes. How do you feel about this syntax?

Copy link
Contributor

@konnov konnov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few comments to the new version. Thank you for producing a pdf. It's way easier to read!

Comment on lines 47 to 50
pure def flatMap(r:Erc20Result, f:Erc20State => Erc20Result): Erc20Result = r match {
Ok(s) => f(s),
Error(_) => r
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we not call it flatMap? We want a friendly specification language :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe “niceMap”? Lol 😂


// Example of a sum type representing a
// NOTE: Once we support parametric sum-types we can generalize this alias and the map operators
type Erc20Result = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like it. But now I can see that we might want to introduce enum and struct at some point to disambiguate the types. We could make these keywords optional. Syntactically, the types are still different, but it may be a bit too hard to spot the difference without a helper keyword.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that is really the worry I have too. I think the | is really a nice option. Again, people in type script don’t seem to have any problem with it.

Having optional keywords is not a good idea imo: it means inconsistent code for readers and unnecessary variance on trivial things. If we must add keywords, let’s please make them required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To help us get a clear and holistic vision of how all the bits of syntax fit together in our (and other) languages, I'll put together a table comparing the different constructs in different languages.

I am worried that if we try to over-index on superficial similarity to rust one corner of the language, we will lose the bigger picture, and its import all our syntax works together. Rust's syntax for sum types makes particular decisions in light of other aspects of the syntax, many of which we don't share. So, imo, we should make sure if adopt rust-isms that they fit with the rest of our syntax, or that we make needed adjustments to rustify other aspects of our language too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In discussion with the team last week, all three of us agreed that the | based syntax seemed most sensible given the rest of our syntax. I've expanded the RFC to discuss the trade offs in some depth, and I've outlined some considerations on how we could change our syntax if we want to pursue a more rust-like syntax.

For the short term, I've moved forward with | for the concrete syntax, but we are not considering it stable at all at this point.

doc/rfc001-erc-sum-types-example.qnt Outdated Show resolved Hide resolved
Comment on lines 56 to 58
pure def A(x:int): T = variant("A", x)
pure def B(x:str): T = variant("B", x)
pure def C(): T = variant("C", {})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to generate A, B, C, or T::A, T::B, T::C (similar to rust)? If we strive for minimality, probably the former

Copy link
Contributor Author

@shonfeder shonfeder Jul 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter would conflict with our module system notation: it would not be clear from syntax whether you were accessing a module operator.

If we supported nested modules, we could generate a module with operators for the constructors, but we don’t support that. So I don’t know how we could resolve that ambiguity. I also don’t see the benefit for practically to that syntax Do you have anything in mind?

Copy link
Contributor Author

@shonfeder shonfeder Jul 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaict, Rust doesn’t have that ambiguity because their modules names must be lower case. function names must also be lower case, upper case is reserved for types and data constructors.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only worry is that enums tend to produce conflicts. This is probably why languages like Java hide the individual constants under the enum's namespace. I guess, Rust does something similar. Sure, it's not an issue, if the spec write introduces unique names.

@shonfeder shonfeder force-pushed the 244/sum-types-design branch 2 times, most recently from c01c18a to 42e1252 Compare July 27, 2023 18:15
@shonfeder shonfeder changed the title WIP RFC 001: Extend Quint with Row-Polymorphic Sum Types WIP RFC 001: Extend Quint type system so support Sum Types Aug 3, 2023
@shonfeder shonfeder changed the title WIP RFC 001: Extend Quint type system so support Sum Types RFC 001: Extend Quint type system so support Sum Types Aug 5, 2023
Shon Feder and others added 16 commits August 5, 2023 00:20
Also filled in some of the discussion around why we'll eschew
row-polymorphic sum types and considerations re: Rust's syntax.
GitHub recognizes the `$$` demarcater for latex, but not the `\[` that
was being used. Should improve readability!
- Use `|`
- Detail issues with naive adoption of Rust's syntax
- Fix some syntax
@shonfeder shonfeder marked this pull request as ready for review August 5, 2023 04:20
Copy link
Contributor

@konnov konnov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read the syntax part in detail and skimmed through the detailed part, as I have read it before. I agree with the syntax. Let's start with this one. We can always change the grammar later. I actually did not know that Quint admits empty records: {} does not parse as a record, but Rec() does. If we want to rely on Rec() being the unit value/type, we have to emphasise this decision in the language document.

@shonfeder shonfeder changed the title RFC 001: Extend Quint type system so support Sum Types RFC 001: Extend Quint type system to support Sum Types Aug 21, 2023
@shonfeder
Copy link
Contributor Author

If we want to rely on Rec() being the unit value/type, we have to emphasise this decision in the language document.

With the current design this isn't user-facing, and we don't have to rely on this, we could always switch to a different canonical unit. But the empty record works for now, since it will always be isopmorphic to the unit! :)

Maybe we can add this to lant.md and adjust the syntax to support it if we end up funding it ergonomic enough. WDYT?

@shonfeder
Copy link
Contributor Author

Thanks for the review!

@shonfeder shonfeder merged commit 99e35cb into main Aug 21, 2023
15 checks passed
@shonfeder shonfeder deleted the 244/sum-types-design branch August 21, 2023 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Draft ADR on sum types syntax, statics, and dynamics
2 participants