Would this give part of what you're looking for?
#lang racket
(define (my-string-length s)
((or/c (and/c (or/c symbol? string?) (compose1 string-length ~a))
(curry raise-arguments-error 'my-string-length "invalid arg"
"arg"))
s))
(my-string-length "foo")
(my-string-length 'foo)
(my-string-length 7)
It's not something you'd want to write by hand in a lot of places, but it
seems an easy target for a macro.
On Sun, Mar 6, 2022 at 10:47 AM Alexis King <[email protected]> wrote:
> Hello,
>
> As a user of the Racket contract system, I sometimes find myself thinking
> about the potential utility of “coercing” or “canonicalizing” contracts. In
> Racket programs, we often idiomatically allow values to be provided to a
> function in a non-canonical form for the sake of convenience. One example
> is the commonly-used path-string? contract, which is morally equivalent
> to using path? but allows the caller to omit an explicit use of
> string->path. Another example is the commonly-used failure-result/c
> contract, which allows the caller to omit wrapping non-procedures in a
> thunk.
>
> While this idiom does make life easier for one party to the contract, it
> ultimately just transfers the burden of canonicalizing the value to the
> other party. This is unfortunate, because it results in a duplication of
> both logic and work:
>
> -
>
> Code to canonicalize the value must be written separately and kept in
> sync with the contract, which is error-prone.
> -
>
> The value ends up being inspected twice: once to determine if it
> satisfies the contract, and a second time to convert it to canonical form.
>
> (In the nomenclature of a popular blog post I wrote a few years ago, these
> contracts are validating, not parsing
> <https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/>.)
>
> In theory, it is perfectly possible to implement a canonicalizing contract
> using the current contract system. However, such a contract has several
> practical downsides:
>
> -
>
> It is necessarily an impersonator contract, not a chaperone contract.
> This prevents its use in places that demand a chaperone contract, such as
> the *key* argument to hash/c.
> -
>
> It moves actual logic into the contract itself, which means using the
> uncontracted value directly is less convenient. This encourages placing the
> contract boundary close to the value’s definition to create a very small
> contracted region (e.g. via define/contract), even though blame is
> generally more useful when the contract boundary corresponds to a boundary
> between higher-level components (e.g. via contract-out).
> -
>
> There is no way to write such contracts using the combinators provided
> by racket/contract, so they must be implemented via the lower level
> make-contract/build-contract-property API. This can be subtle to use
> correctly, and it makes it unlikely that contract violations made by the
> contract itself will be blamed properly according to the “indy” blame
> semantics used by ->i.
>
> All this is to say that the current contract system clearly discourages
> this use of contracts, which suggests this would be considered an abuse of
> the contract system. Nevertheless, the coupling between validating values
> and converting them to a normal form is so enormously tight that allowing
> them to be specified together remains incredibly compelling. I therefore
> have two questions:
>
> 1.
>
> Has this notion of “canonicalizing” contracts been discussed before,
> whether in informal discussions or in literature?
> 2.
>
> Is there any existing work that explores what adding such contracts to
> a Racket-style, higher-order contract system in a principled fashion might
> look like?
>
> Thanks in advance,
> Alexis
>
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/CAA8dsad%2BZHLQTBK-oa5UZJfV7t33Fk0Q0L5rTG-CBnzMqH3haA%40mail.gmail.com
> <https://groups.google.com/d/msgid/racket-users/CAA8dsad%2BZHLQTBK-oa5UZJfV7t33Fk0Q0L5rTG-CBnzMqH3haA%40mail.gmail.com?utm_medium=email&utm_source=footer>
> .
>
--
You received this message because you are subscribed to the Google Groups
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-users/CAE8gKoeEQYAy6J63MyCxFTmQ6rneUXhsj9DD10-shpwL7GGUFQ%40mail.gmail.com.