large hash/dc errors (was: Contracts for (partially) specifying dictionary key -> value-predicates)

42 views
Skip to first unread message

Ben Greenman

unread,
Oct 30, 2020, 10:16:59 PM10/30/20
to Racket Users
> Ben: if you have an example of the bad hash/dc error message I'd be
> interested to see it.
>
> Robby

Here's the kind of message that turned me off:

```
ctc.rkt:34:0: broke its own contract
promised: (hash/dc (k (or/c (quote a) (quote b) (quote c))) (v (k)
(config-value/c k)) #:immutable #t #:kind (quote flat))
produced: '#hash((a . 1) (b . #t) (c . #<void>))
in: (hash/dc
(k (or/c 'a 'b 'c))
(v (k) (config-value/c k))
#:immutable
#t
#:kind
'flat)
contract from: +
blaming: +
(assuming the contract is correct)
```

It prints the whole hash and contract, leaving me to figure out the
two key parts: (1) what piece of the hash failed, and (2) what the
contract expected for that piece.

Now that I think of it ->* is bad in the same way --- especially for
`plot` functions.


Here's the code that made that error message. The #;(lambda ....)
comment is the contract that I ended up using instead of hash/dc (it
still doesn't explain bad values well).

```
#lang racket/base

(require racket/contract)

(provide config/c)

(define config-key/c
(or/c 'a 'b 'c))

(define (config-value/c k)
(case k
((a)
string?)
((b)
boolean?)
((c)
void?)))

(define config/c
#;(lambda (h)
(for ((k (in-list (list 'a 'b 'c))))
(unless (hash-has-key? h k)
(raise-arguments-error 'config/c "missing key" "key" k "hash" h))
(define v (hash-ref h k))
(unless ((config-value/c k) v)
(raise-arguments-error 'config/c "bad value for key" "key" k
"value" v "hash" h))
(void)))
(hash/dc
[k config-key/c]
[v (k) (config-value/c k)]
#:immutable #true
#:kind 'flat))

(contract
config/c
(hash 'a 1 'b #true 'c (void))
'+
'-)
```

jackh...@gmail.com

unread,
Oct 31, 2020, 6:53:49 AM10/31/20
to Racket Users
I have the same issue for ->i contracts. I'll write multiple #:pre/desc checks with nice messages, which are promptly rendered useless by the fact that ->i prints out the whole contract instead of just the precondition check that failed.

Robby Findler

unread,
Oct 31, 2020, 11:17:52 AM10/31/20
to jackh...@gmail.com, Racket Users
I'm still thinking about Ben's example but I'm seeing the description right at the top in the examples below. What am I missing?

Robby

#lang racket

(module m racket
  (provide
   (contract-out
    [f (->i ([x integer?])
            #:pre/desc (x)
            (or (> x 5)
                "must be larger than 5")
            #:pre/desc (x)
            (or (even? x)
                "must be even")
            any)]))
  (define (f x) x))

(require 'm)

(display
 (exn-message
  (with-handlers ([values values])
    (f 1))))

(newline)
(newline)

(display
 (exn-message
  (with-handlers ([values values])
    (f 7))))



--
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 racket-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/3b0ed32b-4875-4cfa-9b2b-e4398e4318d1n%40googlegroups.com.

jackh...@gmail.com

unread,
Oct 31, 2020, 7:45:08 PM10/31/20
to Racket Users
The message shows up at the top, but the message also includes the whole contract with all the other messages. Error messages that include too much information are frustrating, because I have to scan through the message to look for the important bit. The extra information has a cost. For instance, this is what my REPL looked like after running Robby's program:

Screen Shot 2020-10-31 at 4.29.59 PM.png

Since the message is too big to fit in the REPL panel, I have to scroll around to find the "f: contract violation; must be even" and "f: contract violation; must be larger than five" parts of the message. And the message doesn't say what the actual value of x was!

I think when a precondition of an ->i contract is violated, the message should just include the precondition description and the values of the parameters the precondition depends on. The whole contract shouldn't be printed - I'd prefer it either be omitted entirely or shortened with ellipses. So I think that program should produce output like this:

f: contract violation;

 must be larger than 5
  x: 1
  in: a precondition of (->i ...)
  contract from: (anonymous-module m)
  blaming: anonymous-module

   (assuming the contract is correct)
  at: unsaved-editor:6.5

f: contract violation;
 must be even
  x: 7
  in: a precondition of (-> i ...)
  contract from: (anonymous-module m)
  blaming: anonymous-module

   (assuming the contract is correct)
  at: unsaved-editor:6.5
Reply all
Reply to author
Forward
0 new messages