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

Contracts and Type Annotations #912

Open
vyzo opened this issue Sep 22, 2023 · 14 comments
Open

Contracts and Type Annotations #912

vyzo opened this issue Sep 22, 2023 · 14 comments

Comments

@vyzo
Copy link
Collaborator

vyzo commented Sep 22, 2023

Following some prototype work some years ago in #417, I have reached some conclusions about the functionality that we want.

  • We want def/lambda forms (they don't have to be in the core prelude, it can be a redef from :std/contract or :std/typed).
  • We want to be able to declare both checked contracts and type assertions
    • Checked contracts will have the form (arg :: predicate-expr) and they imply a contract generated check and an assertion propagated once the check passes.
    • Assertions will have the form (arg -: predicate-expr) and will have the form of programmer declarations. The assertions will be propagated as gospell (trust the programmer, hopefully he is not holding his footgun).
    • Return type assertions will have the form >: predicate-expr and provide information for the compiler to propagate return values. Note that it is not reasonable to generate checks for those, as it will break tail recursion. The compiler can however check (or infer) the return value and emit a warning (or error) if the programmer has made a mistake.
  • We want to attach them to interfaces
  • We want the compiler to take action on propagated type assertions and emit check eliding code.
  • We want a :gerbil/typed language prelude that integrates all of the above.
@vyzo vyzo added this to the Gerbil v0.19: Benko milestone Sep 22, 2023
@vyzo vyzo mentioned this issue Sep 22, 2023
3 tasks
@vyzo vyzo changed the title Contracts Contracts and Type Annotations Sep 23, 2023
@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

A better syntax proposal that doesn't use keywords:

  • := for checked contracts
  • :- for type assertions
  • :> for return types

So here is a definition for a procedure that mixes all of them:

(def (do-something (a := A?) (b :- fixnum?)) :> fixnum?
  ... ; body
)

This could also be attached to an interface:

(interface X
  (method-a (a := A?) (b := fixnum?)) :> fixnum?
  ...)

And method can simply make type assertions because the interface facade has already checked the contracts:

(defmethod {X x-class}
  (lambda (self (a :- A?) (b :- fixnum?)) :> fixnum?
   ...))

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

We can also extend struct and class definitions to add type annotations to members:

(defstruct X ((a :- fixnum?) ...) ...)
(defclass X ((a :- fixnum?) ...) ...)

@fare
Copy link
Collaborator

fare commented Sep 25, 2023

: or :: is a widely accepted standard for type annotations (including return type annotations), and I see no reason to depart from it. For other annotations, sure do our own things.

@fare
Copy link
Collaborator

fare commented Sep 25, 2023

Don't we need a regular prefix syntax, too? Or are we becoming Rhombus? Better read the Rhombus paper, then... is it out?

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

Yes, the prefix syntax will be raw annotation: (begin-annotation (type ...) expr).

The :std/contract macros will expand to that.

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

ok, fair enough, we can us :: for return type.

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

although...
I don't want a keyword really, I want a syntactic token.

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

: is unclear -- is it contract or type assertion? We have both, hence the discriminant with :- and :=.

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

Return type annotation can also use :- and := -- Semantics:

  • :- is assertion, compiler just trusts you
  • := is soft, compiler will verify the return type statically and reject if it doesn't work

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 25, 2023

Note that this opens the door for the inevitable dependent types further down the road.

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 26, 2023

Following discussion with fare, we have reached consensus, although we are not sure about the exact assertion operator name.

The current proposal:

  • : for checked type contracts, both for input and output parameters. The compiler will perform (best effort) checks at compile time, and if the type contract is not satisfied, it will emit an error. If the contract is satisfied at compile time, it should generate code that avoids the runtime check. If it is unknown, then the contract will be checked at runtime for input parameter. We also have the option of checking output parameters at call sites, perhaps we could enable this with a strict compilation option.
  • :- for unchecked type assertions. If the compiler sees a violation at compile time, it will emit a warning.

@vyzo
Copy link
Collaborator Author

vyzo commented Sep 26, 2023

Other possible symbols for type assertions (@fare wants to uglify them, I want to keep them tidy):

  • :~
  • :&
  • :!

This was referenced Sep 26, 2023
@vyzo
Copy link
Collaborator Author

vyzo commented Sep 28, 2023

Preliminaries in #934; here is the syntactic tokens we settled on:

  • : checked type declaration; it will emit an instance check at the boundary.
  • :~ checked predicate contract; it will emit a predicate check at the boundary.
  • :- unchecked type assertion; the big gun, hopefully unsuitable for feet.

vyzo added a commit that referenced this issue Sep 29, 2023
Baby steps towards contracts and type annotations, with the first
benefits: `obj.method` syntax for calling interface methods.

See #912 for the full picture, which is slated for Benko.

Summary of changes:
- [x] introduces `using` which handles contract and type annotations for
identifiers, and (most importatly) _dotted notation_ for interface
method calls and struct/class field/slot access/mutation.
- [x] introduces contract violation errors and removes BadArgument;
unnecessary at this point.
- [x] adds contracts to interface definitions.
- [x] introduces dotted notation for interface method calls in the
context of `with-type`
- [x] refactors the stdlib to use the new goodness.

TBD:
- [x] documentation
- [x] run the postgres test to make sure nothing broke from the
refactoring.
- [x] add some more interface tests.
@vyzo vyzo linked a pull request Apr 18, 2024 that will close this issue
@vyzo
Copy link
Collaborator Author

vyzo commented May 5, 2024

this is mostly done with types gerbil, awaiting the v0.18.2 release.

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

No branches or pull requests

2 participants