[racket-dev] Feature request - contract form that splits provide/contract into two parts
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
>> <matthias at 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