1
0
Fork
You've already forked tinycontract
0
No description
Scheme 100%
Find a file
2025年01月08日 13:31:52 +01:00
COPYING.txt Initial commit 2025年01月08日 13:31:52 +01:00
README.md Initial commit 2025年01月08日 13:31:52 +01:00
tinycontract.sls Initial commit 2025年01月08日 13:31:52 +01:00

A tiny library for contract-like type checks on Scheme procedures

Probably not ‘true’ contracts because they are not only enforced at module boundaries.

Uses continuation marks (SRFI 226) to only enforce output value type checks when doing so would not interfere with a tail call.

(lambda/contract <formals> (-> <input-contracts> <output-contracts>) <body>)
<input-contracts> --> <value-contract>
 | (<value-contract> ...)
 | (<value-contract ... . <value-contract>)
<output-contracts> --> <value-contract>
 | (<value-contract> ...)
 | (<value-contract ... . <value-contract>)
<value-contract> --> <expression>

There must be as many input contracts as formals. When called, each argument will be checked with the procedures resulting from the evaluation of the corresponding input contract expressions.

When the procedure is called outside of the tail position of a contracted procedure (be it the same procedure or another), the output values will additionally be checked with the procedures resulting from the evaluations of the corresponding output contract expressions.

The ‘rest’ value contract is applied to each rest value individually, in both input and output.

Trivial example

(define even->odd
 (lambda/contract (n)
 (-> (even?) (odd?))
 (+ n 1)))

(even->odd 4) returns 5. (even->odd 5) is a violation.

Acknowledgements

The code that detects a tail call is based on Tony Garnock-Jones’s example.

Bugs

The <value-contract> expressions are evaluated anew every time the checks are run, instead of once when the procedure is created.

Probably many others. This is likely not a sensible approach to this problem.