Search code examples
racketdatacontractcontract

Contracts: How to Accept Functions of Any Matching Signature?


I'm trying to write this contract: "accept a function that takes at least one argument, and returns a boolean."

The key thing is that I only care that it returns a boolean, and that it accepts one or more arguments -- for instance, it could be a function accepting a single argument, or a function accepting 2 arguments, and either should be accepted.

Here's what I've tried:

First attempt:

(-> any/c any/c ... boolean?)

Second attempt:

(->* (any/c)
     (any/c ...) ; => syntax error
     boolean?)

Third attempt:

(->* (any/c)
     #:rest (listof any/c)
     boolean?)

In each of these cases, it seems the contract is expecting the argument not to just be entailed in the contract specification but to match it exactly. That is, for instance, it must be a function that accepts arbitrarily many arguments instead of "if it's a function accepting 2 arguments, that's fine because I'm accepting functions that take an arbitrary number of arguments."

How does one write such a contract?


Solution

  • Would this work?

    (define/contract (f x)
      (and/c (unconstrained-domain-> boolean?)
             (lambda (p)
               (let loop ([n (procedure-arity p)])
                 (match n
                   [(? number? n) (>= n 1)]
                   [(arity-at-least n) (>= n 1)]
                   [(list xs ...) (ormap loop xs)]))))
      #t)