Chris Vig
Chris Vig

Reputation: 8802

Racket - can a contract on a function argument depend on the value of another argument?

Let's say I have a function:

(define (func x y)
    ...)

I would like to constrain the arguments so that:

The first constraint is obviously trivial, but is there any way to set up the second constraint with Racket's contract system?

Upvotes: 2

Views: 140

Answers (1)

Alexis King
Alexis King

Reputation: 43892

Yes. You want to use dependent contracts, in Racket expressed using the ->i contract combinator. The contract for your function as you described it would look like this:

(->i ([x (and/c integer? positive?)]
      [y (x) (and/c integer? positive? (<=/c x))])
     [result any/c])

Here’s an example of the kind of error that would occur when inappropriately applying the function contracted with the above contract:

> (func 1 2)
func: contract violation
  expected: (and/c integer? positive? (<=/c 1))
  given: 2
  in: the y argument of
      (->i
       ((x (and/c integer? positive?))
        (y
         (x)
         (and/c integer? positive? (<=/c x))))
       (result any/c))
  contract from: (function func)

For dependent contracts, all arguments and the result need to be named so that they can be referred to in other clauses. The (x) within the second argument clause specifies that the contract for y depends on the value of x, so you can use x within the contract specification for y.

The full grammar for ->i is available in the documentation, which has plenty of additional features. It also has some examples, including one that is very similar to the example in your question.

Upvotes: 5

Related Questions