1

First, I apologize for the beginner question. I am an experienced developer, but new to Scheme. I have created a contract requiring a positive integer, but when I provide a real number, the contract is not violated:

(define/contract (listofOne n)
  (-> (and integer? positive?) (listof integer?))
  (cond
    ((= n 0) '())
    (else (cons 1 (listofOne (- n 1))))))

(listofOne 12.5)

I expected the contract to be violated, but instead I got an infinite loop and a buffer overflow. Why did the contract remain unviolated? The first query in my predicate was integer?, so I don't see how the contract could have returned true with an input of 12.5.

EDIT: Just to clarify, I am not looking for a way to make the contract violate. I already know that I can use and/c, and (thanks to @soegaard) that I can reverse positive? and integer? to get it to violate. What I'm looking for at this point is to understand what is going on here.

Thanks in advance for the help!

Ben I.
  • 1,065
  • 1
  • 13
  • 29

1 Answers1

4

UPDATE

I totally missed that you used and and not and/c in your example. Try this:

#lang racket
(define/contract (listofOne n)
  (-> (and/c integer? positive?) (listof integer?))
  (cond
    ((= n 0) '())
    (else (cons 1 (listofOne (- n 1))))))

(listofOne 12.5)

Result:

listofOne: contract violation
  expected: integer?
  given: 12.5
  in: an and/c case of
      the 1st argument of
      (->
       (and/c integer? positive?)
       (listof integer?))
  contract from: (function listofOne)
  blaming: anonymous-module
   (assuming the contract is correct)
  at: unsaved-editor:2.18

SECOND UPDATE

Here is an explanation of and.

The form

(and c1 c2)

means:

1. Evaluate `c1` giving a value `v`
2. If the value `v1` is false, 
   then the result of the `and`-expression is false.
   (note that `c2` is not evaluated)
3. If the value `v1` is non-false, 
   then evaluate the expression `c2` giving a value `v2`.
4. The result of the and-expressions is v2.

Note: If c1 evaluates to true, then (and c1 c2) gives the same result as c2. This means in particular that if c1 is a contract (which is a non-false value) then (and c1 c2) gives the same result as c2.

In your example (and integer? positive?) gives the same result as positive?.

Note also that, this implies that (-> (and integer? positive?) (listof integer?)) works the same as (-> positive? (listof integer?)).

In code:

(and c1 c2)

is the same as

(let ([v1 c1])
  (if v1
      (let ([v2 c2])
         v2)
      #f))

Since you want a contract that uses both c1 and c2 we need a different approach. Let's examine how we can combine two predicates to a simple predicate.

(and/p p1 p2)

Should be short for

(lambda (x)
  (and (p1 x) (p2 x)))

Here and is used on the values returned by the predicates - not on the predicates themselves.

The construct and/c works similar to and/p but the representation of contracts is more involved than predicates. The principle is the same though.

is short for

(let ([t c1])
  (if t
      t
      c2))
soegaard
  • 30,661
  • 4
  • 57
  • 106
  • Does and/c pass a boundary? I get a contract violation with and/c. I have read the explanation for and/c about 10 times now and I'm still not understanding what it does. – Ben I. Oct 21 '16 at 19:53
  • Sorry! I missed that you used `and` instead of `and/c` in the contract. – soegaard Oct 21 '16 at 19:58
  • I see that it works with and/c, but... why? Why doesn't it work for and, but it works for and/c? – Ben I. Oct 21 '16 at 20:52
  • Well, `and` works with expressions and `and/c` works with contracts. Hmm. Let's see what happens if we use `and`. In `(and c1 c2)` where `c1` and `c2` evaluates to contracts, since `and` is "short-circuiting" we have that `(and c1 c2)` evaluates to the value of `c1`. The contract `c2` is thusly ignored. The construct `and/c` on the other hand checks both contracts and checks that both contracts are fulfilled. – soegaard Oct 21 '16 at 20:55
  • @soegaard `and` should only be able to short-circuit on `#f`, not on `#t`. https://en.wikipedia.org/wiki/Short-circuit_evaluation – Ben I. Oct 22 '16 at 00:06
  • You are right. I mixed up c1 and c2. The expression (and c1 c2) will return c2. In your example c2 is positive? – soegaard Oct 22 '16 at 00:09
  • Good point - but note that the expression `integer?` is a variable reference - not a symbol. – soegaard Oct 22 '16 at 00:30
  • Sure enough, when I switch the two arguments, I achieve a contract violation, but why is it ignoring c1? – Ben I. Oct 22 '16 at 00:44
  • I upvoted your answer because it was helpful, but the crux of the question is really about the behavior of `and`, not about getting the contract to violate. I still really don't understand why `and` didn't work. – Ben I. Oct 25 '16 at 15:11
  • @Choirbean I have added a detailed explanation. – soegaard Oct 25 '16 at 21:28
  • I accepted the answer because it is beautifully explained, though I am still actually confused about one tiny part: When looking at `(and c1 c2)`, why is `c1` necessarily a non-false value, while `c2` can evaluate to false? – Ben I. Oct 26 '16 at 19:47
  • It is non-false if `c1` is a contract such as `integer?` or `positive?` -- remember that `(and integer? ...)` tests whether `integer?` is non-false -- it doesn't test any values returned by `integer?`. – soegaard Oct 26 '16 at 19:49
  • But `c2` is a similar boolean statement. Would `(and c1 c2 c3 c4 c5 c6)` just be the same as `(and c6)`, with the first contracts always being true, and the last one evaluated as a boolean statement with a return? – Ben I. Oct 26 '16 at 19:51
  • If it is known that c1 to c5 are contracts (and thus non-false values) then yes. If c1 to c6 are arbitrary expressions, then no. – soegaard Oct 26 '16 at 19:53
  • You must be a saint to still be helping me after all of this time. I know we typically avoid 'thank-you's on this site, but... you really deserve one. Thank you so very much! – Ben I. Oct 26 '16 at 19:56