#lang typed/racket/base
(provide make-compose
compose-3 compose-4)
(require (for-syntax racket/base
racket/match
racket/list
racket/syntax
syntax/parse))
(define-for-syntax (make-compose-type n)
(with-syntax* ([(t ...) (generate-temporaries (make-list n 't))]
[a (generate-temporary 'a)]
[(_ ... t₀) #'(a t ...)]
[(F ...)
(let step ([u #'a] [ts (syntax->list #'(t ...))])
(match ts
['() '()]
[(cons t ts*) (cons #`(#,t → #,u) (step t ts*))]))])
#'(∀ (a t ...) (F ... → t₀ → a))))
(define-syntax make-compose
(syntax-parser
[(_ n:nat)
(with-syntax* ([(f ...) (generate-temporaries (make-list (syntax-e #'n) 'f))]
[x (generate-temporary 'x)]
[T (make-compose-type (syntax-e #'n))]
[body (foldr (λ (fᵢ res) #`(#,fᵢ #,res)) #'x (syntax->list #'(f ...)))])
#'(ann (λ (f ...) (λ (x) body)) T))]))
(define compose-3 (make-compose 3))
(define compose-4 (make-compose 4))
;; and so on