119 lines
4.2 KiB
Racket
119 lines
4.2 KiB
Racket
;;; typed-compose.rkt
|
|
;;;
|
|
;;; Copyright 2020 Sergiu Ivanov <sivanov@colimite.fr>
|
|
;;;
|
|
;;; This program is free software: you can redistribute it and/or
|
|
;;; modify it under the terms of the GNU General Public License as
|
|
;;; published by the Free Software Foundation, either version 3 of the
|
|
;;; License, or (at your option) any later version.
|
|
;;;
|
|
;;; This program is distributed in the hope that it will be useful,
|
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
;;; General Public License for more details.
|
|
;;;
|
|
;;; You should have received a copy of the GNU General Public License
|
|
;;; along with this program. If not, see
|
|
;;; <https://www.gnu.org/licenses/>.
|
|
|
|
#lang typed/racket/base
|
|
|
|
(require syntax/parse/define
|
|
(for-syntax racket/base
|
|
racket/match
|
|
racket/list
|
|
racket/syntax
|
|
syntax/parse))
|
|
|
|
(provide make-compose
|
|
compose-n
|
|
compose-3 compose-4 compose-5 compose-6 compose-7 compose-8 compose-9
|
|
multi-compose multi-chain)
|
|
|
|
(module+ test
|
|
(require typed/rackunit))
|
|
|
|
(: compose-n (All (a) (-> (-> a a) * (-> a a))))
|
|
(define (compose-n . funcs)
|
|
(λ (x)
|
|
(for/foldr ([x x]) ([f funcs])
|
|
(f x))))
|
|
|
|
(module+ test
|
|
(test-case "compose-n"
|
|
(check-equal? ((compose-n add1 add1 add1) 3) 6)))
|
|
|
|
(define-for-syntax (make-compose-type n)
|
|
(with-syntax* ([(t ...) (generate-temporaries (make-list n 't))]
|
|
[(_ ... 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-parser make-compose
|
|
[(_ n:nat)
|
|
(with-syntax* ([(f ...) (generate-temporaries (make-list (syntax-e #'n) 'f))]
|
|
[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))
|
|
(define compose-5 (make-compose 5))
|
|
(define compose-6 (make-compose 6))
|
|
(define compose-7 (make-compose 7))
|
|
(define compose-8 (make-compose 8))
|
|
(define compose-9 (make-compose 9))
|
|
|
|
(module+ test
|
|
(test-case "compose-3 to compose-9"
|
|
(define (s->n [s : String]) (cast (string->number s) Number))
|
|
(define (n->s [n : Number]) (number->string n))
|
|
(check-equal? ((compose-3 n->s add1 s->n) "3") "4")
|
|
(check-equal? ((compose-4 n->s add1 add1 s->n) "3") "5")
|
|
(check-equal? ((compose-5 n->s add1 add1 add1 s->n) "3") "6")
|
|
(check-equal? ((compose-6 n->s add1 add1 add1 add1 s->n) "3") "7")
|
|
(check-equal? ((compose-7 n->s add1 add1 add1 add1 add1 s->n) "3") "8")
|
|
(check-equal?
|
|
((compose-8 n->s add1 add1 add1 add1 add1 add1 s->n) "3") "9")
|
|
(check-equal?
|
|
((compose-9 n->s add1 add1 add1 add1 add1 add1 add1 s->n) "3") "10")))
|
|
|
|
(define-syntax (multi-compose stx)
|
|
;; Implementation by Sorawee Porncharoenwase.
|
|
(syntax-parse stx
|
|
[(_ f:expr g:expr)
|
|
#'(compose f g)]
|
|
[(_ f:expr funcs:expr ...)
|
|
#'(compose f (multi-compose funcs ...))]))
|
|
|
|
(module+ test
|
|
(test-case "multi-compose"
|
|
(check-equal? ((multi-compose add1
|
|
(λ ([x : Number]) (* x 3))
|
|
add1
|
|
(λ ([x : Number]) (+ x 2)))
|
|
3)
|
|
19)))
|
|
|
|
(define-syntax (multi-chain stx)
|
|
(syntax-parse stx
|
|
[(_ funcs:expr ...)
|
|
(define rev-funcs (reverse (syntax->list #'(funcs ...))))
|
|
#`(multi-compose #,@rev-funcs)]))
|
|
|
|
(module+ test
|
|
(require racket/port)
|
|
(test-case "multi-chain and multi-compose"
|
|
(define f1 (λ ([x : Number]) (displayln "f1") (+ x 1)))
|
|
(define f2 (λ ([x : Number]) (displayln "f2") (+ x 1)))
|
|
(define f3 (λ ([x : Number]) (displayln "f3") (+ x 1)))
|
|
(check-equal?
|
|
(with-output-to-string (λ () (displayln ((multi-chain f1 f2 f3) 3))))
|
|
"f1\nf2\nf3\n6\n")
|
|
(check-equal?
|
|
(with-output-to-string (λ () (displayln ((multi-compose f1 f2 f3) 3))))
|
|
"f3\nf2\nf1\n6\n")))
|