The `plot' library uses `unstable/latent-contract' to do exactly that.
Example: the identity function restricted to `integer?' values:
#lang racket
(module internal-provider racket
(require unstable/latent-contract/defthing)
(provide int-id int-id:doc) ; export doc generator too
(defproc (int-id [x integer?]) integer?
x))
(module external-provider racket
(require (submod ".." internal-provider)
unstable/latent-contract)
(provide (activate-contract-out int-id)))
(require (prefix-in internal: 'internal-provider))
(internal:int-id 'x) ; no error
(require 'external-provider)
(int-id 'x) ; contract error blaming 'external-provider
The `activate-contract-out' provide transformer expands to a
`contract-out'. IIRC, `int-id' is an applicable struct whose instances
carry contract information.
Pros:
1. Easily allows using either contracted or uncontracted values
2. Puts contracts near function definition
3. Keeps documented contracts in sync with actual contracts:
#lang scribble/manual
@(require unstable/latent-contract/defthing
(submod "../private/int-id.rkt" internal-provider))
@doc-apply[int-id]{Returns @racket[x].}
Cons:
A. Often gets awkward with higher-order functions
B. Keeps contracts away from the tops of modules
C. Function argument names become part of the documented interface
There are also forms for defining parameters and regular values.
I think having a `declare-contract' form would possibly solve (2) (you
could move them to the top of the file when the library is stable) and
possibly help with (A). But it would make it hard, if not impossible, to
generate documentation.
Also, I'd want to call it `:' instead of `declare-contract' to make it
more obvious how to convert untyped programs to Typed Racket.
I hadn't documented `unstable/latent-contract' yet because I considered
it too experimental. It's worked really well in `plot', though. In
particular, automatically generating documentation has been great
because so many documented functions have such large contracts.
Neil ⊥
On 12/14/2012 09:55 AM, Ryan Culpepper wrote:
I understood the feature request differently. I take it as wanting to
write a module like the following:
#lang racket
(provide
(with-declared-contracts-out
f
g))
(declare-contract f ....)
(define f ....)
(declare-contract g ....)
(define g ....)
That is, the contracts themselves are near the functions, so it's easy
to keep them in sync. And one can find the names provided by just
looking at the top of the module---but you only get the names, not the
contracts, so the interface is incomplete.
This should be easily doable with some compile-time state.
Ryan
On 12/14/2012 11:41 AM, Greg Hendershott wrote:
Matthias has vastly more experience and perspective in matters like
this; you would be wise to prefer his advice.
But if I understand correctly what you want, I think you could do this
yourself with a simple macro:
(define-syntax define/contract/provide
(syntax-rules ()
[(_ (id . args) contract body ...)
(begin
(define/contract (id . args) contract body ...)
(provide id))]
[(_ id contract expr)
(begin
(define/contract id contract expr)
(provide id))]))
That's the version I've used in some past projects. However: It uses
define/contract promiscuously, which is (a) not universally beloved
and (b) much slower than provide/contract in the current version of
Racket.
Instead I think you could flip it around to use provide/contract,
instead:
(define-syntax define/provide/contract
(syntax-rules ()
[(_ (id . args) contract body ...)
(begin
(provide/contract (id . args) contract body ...)
(define id))]
[(_ id contract expr)
(begin
(provide/contract id contract expr)
(define id))]))
I _think_ that's essentially what you're asking for?
(Of course this means the function won't be protected by a contract
when called from inside the module, which could be considered bad,
good, or N/A. Just be aware.)
Any approach not using a single provide/contract list has the
disadvantage that Matthias explained: The module no longer has a
contiguous description of its interface in the source.
That's a serious disadvantage. Some might consider it mitigated if the
module has Scribble documentation. Also someone reading the source
might "collapse" in Emacs (or whatever) to quickly see all the
top-level definitions (although that would include any non-provided
items, so it's not perfect (unless the stock collapse is customized)).
You could also put a list in comments, I suppose.
On Fri, Dec 14, 2012 at 10:44 AM, Matthias Felleisen
<matth...@ccs.neu.edu> wrote:
It is critical to inform clients of the services that a module
provides. In the absence of types, contracts are the closest
information we have. Reading the implementation is against
all good SE ideas.
IF we could assume that people always programmed in DrRacket,
we could compromise and add a tool that synthesizes the interface
of a module in some way. Since some contributors break this guideline
all the time anyway, we should have such a tool available anyway.
BUT there are also people who use Emacs and the other editor.
So, if you want to be good, put provide contract-out at the top
of your module.
On Dec 14, 2012, at 1:02 AM, Harry Spier wrote:
If you place provide/contract at the beginning of a module it makes
the interface clear but it is separated from its function. If you
place it right before its function and not at the top of the module,
it makes the function clearer but the module interface is not so
clear.
Is it possible (would it be a good idea?) to provide a new form that
splits the provide/contract form into two parts. One part the
contract definition which you could place immediately before the
function definition and the second part a provide spec for inclusion
in a provide statement that included the function name but not the
contract spec.) . That way the contract definition can be by the
function definition and the function name can be in a provide
statement at the beginning of the module.
Harry Spier
_________________________
Racket Developers list:
http://lists.racket-lang.org/dev
_________________________
Racket Developers list:
http://lists.racket-lang.org/dev
_________________________
Racket Developers list:
http://lists.racket-lang.org/dev
_________________________
Racket Developers list:
http://lists.racket-lang.org/dev
_________________________
Racket Developers list:
http://lists.racket-lang.org/dev