#lang scheme
(define affine-procedure/c
(make-proj-contract
'affine-procedure/c
(lambda (pos neg src name)
(lambda (f)
(if (procedure? f)
(let ([blessed #t])
(make-keyword-procedure
(lambda (keys vals . args)
(if blessed
(begin
(set! blessed #f)
(keyword-apply f keys vals args))
(raise-contract-error
f
src
neg
name
"affine function applied more than once")))))
(raise-contract-error
f
src
pos
name
"expected affine procedure, given: ~e"
f))))
procedure?))
(define-syntax -o
(syntax-rules ()
[(-o ARGS ...) (and/c (-> ARGS ...) affine-procedure/c)]))
(define-syntax -o*
(syntax-rules ()
[(-o* ARGS ...) (and/c (->* ARGS ...) affine-procedure/c)]))
(provide/contract
[affine-procedure/c contract?])
(provide -o -o*)
(define make-affine-box-type
(case-lambda
[(type-name)
(define-values (affine-box make-affine-box affine-box?
affine-box-ref set-affine-box!)
(make-struct-type type-name
#f
1
0))
(define (rename fmt proc)
(procedure-rename proc
(string->symbol
(format fmt type-name))))
(values
(rename "make-~a"
(lambda (val) (make-affine-box (lambda () val))))
(rename "~a?"
affine-box?)
(rename "~a-ref"
(lambda (ab) ((affine-box-ref ab 0))))
(rename "~a/c"
(lambda (c)
(let ([ctc (coerce-contract 'affine-box/c c)])
(make-proj-contract
(build-compound-type-name 'affine-box/c c)
(lambda (pos neg src name)
(lambda (ab)
(if (affine-box? ab)
(let ([blessed #t])
(make-affine-box
(lambda ()
(if blessed
(begin
(set! blessed #f)
((((proj-get ctc) ctc) pos neg src name)
((affine-box-ref ab 0))))
(raise-contract-error
ab
src
neg
name
"~a unboxed more than once"
type-name)))))
(raise-contract-error
ab
src
pos
name
"expected ~a, given: ~e"
type-name
ab))))
affine-box?)))))]
[(type-name contract)
(let-values ([(box box? box-ref box/c)
(make-affine-box-type type-name)])
(values
box box? box-ref (box/c contract)))]))
(define-syntax define-affine-box
(lambda (stx)
(syntax-case stx ()
[(define-affine-box type-name rest ...)
(begin
(define (name fmt)
(datum->syntax
stx
(string->symbol
(format fmt (syntax->datum (syntax type-name))))
stx))
(identifier? (syntax type-name))
(quasisyntax
(define-values
((unsyntax (name "make-~a"))
(unsyntax (name "~a?"))
(unsyntax (name "~a-ref"))
(unsyntax (name "~a/c")))
(make-affine-box-type 'type-name rest ...))))])))
(define-affine-box affine-box)
(provide/contract
[make-affine-box (any/c . -> . affine-box?)]
[affine-box? (any/c . -> . boolean?)]
[affine-box-ref (affine-box? . -> . any/c)]
[affine-box/c (contract? . -> . contract?)])
(provide
make-affine-box-type
define-affine-box)