#lang scheme/base
(require (for-syntax scheme/base))
(define (identity x) x)
(define (make-keyword-thunk f)
(make-keyword-procedure f f))
(define-syntax (thunk stx)
(syntax-case stx ()
[(thunk body ...)
(syntax/loc stx
(make-keyword-thunk (lambda _ body ...)))]))
(define call
(make-keyword-procedure
(lambda (keys vals f . args) (keyword-apply f keys vals args))
(lambda (f . args) (apply f args))))
(define (constant v) (thunk v))
(define (curry*/r? sym r?)
(let* ([append-args
(if r?
(lambda (args1 args2) (append args2 args1))
(lambda (args1 args2) (append args1 args2)))])
(make-keyword-procedure
(lambda (keys1 vals1 f . args1)
(make-keyword-procedure
(lambda (keys2 vals2 . args2)
(let* ([alist1 (map cons keys1 vals1)]
[alist2 (map cons keys2 vals2)]
[alist (keyword-merge sym f alist1 alist2)]
[keys (map car alist)]
[vals (map cdr alist)]
[args (append-args args1 args2)])
(keyword-apply f keys vals args)))
(lambda args2
(keyword-apply f keys1 vals1 (append-args args1 args2)))))
(lambda (f . args1)
(make-keyword-procedure
(lambda (keys2 vals2 . args2)
(keyword-apply f keys2 vals2 (append-args args1 args2)))
(lambda args2 (apply f (append-args args1 args2))))))))
(define curry* (curry*/r? 'curry* #f))
(define curryr* (curry*/r? 'curryr* #t))
(define (keyword-merge sym f one two)
(cond
[(null? one) two]
[(null? two) one]
[(keyword<? (caar one) (caar two))
(cons (car one) (keyword-merge sym f (cdr one) two))]
[(keyword<? (caar two) (caar one))
(cons (car two) (keyword-merge sym f one (cdr two)))]
[else
(error sym
"~s: keyword ~s has redundant values ~s and ~s"
(or (object-name f) '<unknown>)
(caar one) (cadr one) (cadr two))]))
(define (conjoin . fs)
(make-keyword-procedure
(lambda (keys vals . args)
(andmap (lambda (f) (keyword-apply f keys vals args)) fs))
(lambda args
(andmap (lambda (f) (apply f args)) fs))))
(define (disjoin . fs)
(make-keyword-procedure
(lambda (keys vals . args)
(ormap (lambda (f) (keyword-apply f keys vals args)) fs))
(lambda args
(ormap (lambda (f) (apply f args)) fs))))
(define-syntax (lambda/parameter/derived stx)
(syntax-case stx ()
[(lpd orig (arg . rest) lets args params body)
(identifier? #'arg)
(syntax/loc stx
(lpd orig rest lets (arg . args) params body))]
[(lpd orig ([arg default] . rest) lets args params body)
(identifier? #'arg)
(syntax/loc stx
(lpd orig rest lets ([arg default] . rest) params body))]
[(lpd orig ([arg #:param expr] . rest) lets args params body)
(identifier? #'arg)
(syntax/loc stx
(lpd orig rest
([it expr] . lets)
([arg (it)] . args)
([it arg] . params)
body))]
[(lpd orig (key arg . rest) lets args params body)
(and (keyword? (syntax-e #'key)) (identifier? #'arg))
(syntax/loc stx
(lpd orig rest lets (key arg . args) params body))]
[(lpd orig (key [arg default] . rest) lets args params body)
(and (keyword? (syntax-e #'key)) (identifier? #'arg))
(syntax/loc stx
(lpd orig rest lets (key [arg default] . rest) params body))]
[(lpd orig (key [arg #:param expr] . rest) lets args params body)
(and (keyword? (syntax-e #'key)) (identifier? #'arg))
(syntax/loc stx
(lpd orig rest
([it expr] . lets)
(key [arg (it)] . args)
([it arg] . params)
body))]
[(lpd orig () lets args params body)
(syntax/loc stx
(let lets (lambda args (parameterize params . body))))]
[(lpd orig (key arg . rest) lets args params body)
(keyword? (syntax-e #'key))
(raise-syntax-error #f "invalid keyword argument" #'orig #'arg)]
[(lpd orig (arg . rest) lets args params body)
(raise-syntax-error #f "invalid positional argument" #'orig #'arg)]
[(lpd orig . _)
(raise-syntax-error #f "error in expansion" #'orig)]))
(define-syntax (lambda/parameter stx)
(syntax-case stx ()
[(lp (clause ... . rest) . body)
(quasisyntax/loc stx
(lambda/parameter/derived #,stx (clause ...) () rest () body))]))
(provide
identity constant conjoin disjoin call curry* curryr*
thunk lambda/parameter)