If I were to get to redo racket/contract, the one big change I'd make is to design a (macro-based) DSL for contracts instead of going with a combinator-based approach. Although you won't need to do it for a minimum-viable product, the opportunity to, at a later date, look at an entire contract at compile time and generate better code for it is probably going to be useful.
That is, no one writes things like (apply listof (map -> doms rngs))
or things like that; instead people tend to write out their contracts directly (-> (listof integer?) boolean?)
or similar. So the fact that ->
and listof
are just normal functions isn't really used and it leads to an opportunity cost of being able to generate a function contract "all at once" that's tailored to the specifics of the domain and range of the function.
Of course the contract library is able to do some of this already as it makes ->
and listof
etc behave like functions but actually be macros but it is a pain and it would be a lot better if there was an explicit (sub)-language designed. Then, all the indirect calls through the late-neg projections could be avoided and performance would be better.
That said, you definitely want to allow arbitrary predicates to just be contracts without having to type a lot of stuff, as that's turned out to be super useful.