;;; typed-compose.rkt ;;; ;;; Copyright 2020 Sergiu Ivanov ;;; ;;; 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 ;;; . #lang typed/racket (require (for-syntax syntax/parse)) (provide 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))) (: compose-3 (All (a b c d) (-> (-> c d) (-> b c) (-> a b) (-> a d)))) (define (compose-3 f1 f2 f3) (λ (x) (f1 (f2 (f3 x))))) (: compose-4 (All (a b c d e) (-> (-> d e) (-> c d) (-> b c) (-> a b) (-> a e)))) (define (compose-4 f1 f2 f3 f4) (λ (x) (f1 (f2 (f3 (f4 x)))))) (: compose-5 (All (a b c d e f) (-> (-> e f) (-> d e) (-> c d) (-> b c) (-> a b) (-> a f)))) (define (compose-5 f1 f2 f3 f4 f5) (λ (x) (f1 (f2 (f3 (f4 (f5 x))))))) (: compose-6 (All (a b c d e f g) (-> (-> f g) (-> e f) (-> d e) (-> c d) (-> b c) (-> a b) (-> a g)))) (define (compose-6 f1 f2 f3 f4 f5 f6) (λ (x) (f1 (f2 (f3 (f4 (f5 (f6 x)))))))) (: compose-7 (All (a b c d e f g h) (-> (-> g h) (-> f g) (-> e f) (-> d e) (-> c d) (-> b c) (-> a b) (-> a h)))) (define (compose-7 f1 f2 f3 f4 f5 f6 f7) (λ (x) (f1 (f2 (f3 (f4 (f5 (f6 (f7 x))))))))) (: compose-8 (All (a b c d e f g h i) (-> (-> h i) (-> g h) (-> f g) (-> e f) (-> d e) (-> c d) (-> b c) (-> a b) (-> a i)))) (define (compose-8 f1 f2 f3 f4 f5 f6 f7 f8) (λ (x) (f1 (f2 (f3 (f4 (f5 (f6 (f7 (f8 x)))))))))) (: compose-9 (All (a b c d e f g h i j) (-> (-> i j) (-> h i) (-> g h) (-> f g) (-> e f) (-> d e) (-> c d) (-> b c) (-> a b) (-> a j)))) (define (compose-9 f1 f2 f3 f4 f5 f6 f7 f8 f9) (λ (x) (f1 (f2 (f3 (f4 (f5 (f6 (f7 (f8 (f9 x))))))))))) (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 (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")))