#lang racket
(require unstable/temp-c/dsl
         tests/eli-tester)

(define manual-strange/c
  (make-contract
   #:name "amazing contract"
   #:first-order procedure?
   #:projection
   (λ (b)
     (λ (x)
       (λ (f)
         (define ready? #f)
         (letrec-values
             ([(? o) 
               (x 
                (λ (y)
                  (cond
                    [(not ready?)
                     (raise-blame-error b x "cannot call until return")]
                    [(not (? y))
                     (raise-blame-error b x "expected a value of ~a" ?)]
                    [else
                     (f y)])))])
           (set! ready? #t)
           (values ? o)))))))

(define hot-strange/c
  (with-monitor
      (label 'strange
             (-> (label 'strategy
                        (-> any/c ; The monitor will make it tighter
                            any/c))
                 (values (-> any/c boolean?)
                         any/c)))
    (complement
     (union
      ; You can't call strategy until strange returns
      (seq (star _) (call 'strange _)
           (star (not (ret 'strange _ _)))
           (call 'strategy _))
      ; You can't call strategy with something that violates the predicate
      (seq (star _) (call 'strange _) (star _)
           (dseq (ret 'strange predicate? _)
                 (seq (star _)
                      (call 'strategy (not (? predicate?))))))))))

(define (try-it-out strange/c)
  (define strange-fun/ctc
    (contract strange/c 
              (λ (f)
                (values number? f))
              'pos 'neg))
  
  (define bad-strange-fun/ctc
    (contract strange/c 
              (λ (f)
                (f 4)
                (values number? f))
              'pos 'neg))
  
  (define-values (? o) (strange-fun/ctc (λ (x) x)))
  (test
   (o 4) => 4
   (o "string") =error> "contract violation"
   
   (bad-strange-fun/ctc (λ (x) x)) =error> "contract violation"))

(test
 ; ->i doesn't work
 (->i ([strategy (predicate?) (-> predicate? any/c)])
      (values [predicate? (-> any/c boolean?)]
              [object any/c]))
 =error>
 "an argument cannot depend on a result"
 
 ; but the manual version does
 (try-it-out manual-strange/c)
 ; and so does the temporal version
 (try-it-out hot-strange/c))
