typed-compose/typed-compose.scrbl

109 lines
4.3 KiB
Plaintext
Raw Normal View History

2020-12-17 20:45:26 +01:00
#lang scribble/manual
@(require scribble/example racket/sandbox
(for-label typed/racket/base typed-compose))
2020-12-17 20:45:26 +01:00
2020-12-17 20:46:27 +01:00
@title{Utilities for composing functions in Typed Racket}
2020-12-17 20:45:26 +01:00
@author[@author+email["Sergiu Ivanov" "sivanov@colimite.fr"]]
@defmodule[typed-compose]
2020-12-17 20:45:26 +01:00
Typed Racket's @racket[compose] only takes two arguments, because in general it
is difficult to specify that the return types and the argument types should be
the same for two successive functions in the argument list. This package
defines some further utilities to allow @racket[compose]-ing more than two
functions more comfortable in Typed Racket.
2020-12-17 21:09:31 +01:00
@(define typed-compose-evaluator
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string]
[sandbox-memory-limit 50])
(make-evaluator 'typed/racket/base #:requires '(typed-compose))))
@section{License}
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
@bold{without any warranty}; without even the implied warranty of
@bold{merchantability} or @bold{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
@hyperlink["https://www.gnu.org/licenses/"]{https://www.gnu.org/licenses/}.
@section{Functions for composing functions}
@defproc[(compose-n [proc (-> a a)] ...) (-> a a)]{
Compose an arbitrary number of functions of type @racket[(-> a a)].
@examples[#:eval typed-compose-evaluator
((compose-n add1 add1 add1) 3)
]}
@defproc*[([(compose-3 [proc1 (-> c d)] [proc2 (-> b c)] [proc3 (-> a b)]) (-> a d)]
[(compose-4 [proc1 (-> d e)] [proc2 (-> c d)] [proc3 (-> b c)] [proc4 (-> a b)]) (-> a e)]
[(compose-5 [proc1 (-> e f)] [proc2 (-> d e)] [proc3 (-> c d)] [proc4 (-> b c)] [proc5 (-> a b)]) (-> a f)]
[(compose-6 [proc1 (-> f g)] [proc2 (-> e f)] [proc3 (-> d e)] [proc4 (-> c d)] [proc5 (-> b c)] [proc6 (-> a b)]) (-> a g)]
[(compose-7 [proc1 (-> g h)] [proc2 (-> f g)] [proc3 (-> e f)] [proc4 (-> d e)] [proc5 (-> c d)] [proc6 (-> b c)] [proc7 (-> a b)]) (-> a h)]
[(compose-8 [proc1 (-> h i)] [proc2 (-> g h)] [proc3 (-> f g)] [proc4 (-> e f)] [proc5 (-> d e)] [proc6 (-> c d)] [proc7 (-> b c)] [proc8 (-> a b)]) (-> a i)]
[(compose-9 [proc1 (-> i j)] [proc2 (-> h i)] [proc3 (-> g h)] [proc4 (-> f g)] [proc5 (-> e f)] [proc6 (-> d e)] [proc7 (-> c d)] [proc8 (-> b c)] [proc9 (-> a b)]) (-> a j)])]{
@racket[compose-i] composes @racket[i] functions. The rightmost function is
applied first.
@examples[#:eval typed-compose-evaluator
(define (s->n [x : String]) (cast (string->number x) Number))
(define fancy-add1 (compose-3 print add1 s->n))
fancy-add1
(fancy-add1 "1")
]}
@section{Macros for composing functions}
@defform[(make-compose n)
#:contracts ([n exact-nonnegative-integer])]{
Expants to a typed @racket[lambda] form composing exactly @racket[n]
one-argument functions. For example, @racket[compose-3] is defined as:
@racket[(define compose-3 (make-compose 3))]. The rest of the functions of the
@racket[compose-i] family are defined using this macro as well.
}
@defform[(multi-compose func ...)
#:contracts ([func expression])]{
Expands to a code applying @racket[compose] in a pairwise manner to the given
expressions. For example, @racket[(multi-compose f1 f2 f3 f4)] expands to
@racket[(compose f1 (compose f2 (compose f3 f4)))].
@examples[#:eval typed-compose-evaluator
((multi-compose add1
(λ ([x : Number]) (* x 3))
add1
(λ ([x : Number]) (+ x 2)))
3)
]}
@defform[(multi-chain func ...)
#:contracts ([func expression])]{
Like @racket[multi-compose], but the first function in the argument list is
applied first instead of last. For example, @racket[(multi-chain f1 f2 f3
f4)] expands to @racket[(compose f4 (compose f3 (compose f2 f1)))].
@examples[#:eval typed-compose-evaluator
(define f1 (λ ([x : Number]) (displayln "f1") (+ x 1)))
(define f2 (λ ([x : Number]) (displayln "f2") (+ x 1)))
(define f3 (λ ([x : Number]) (displayln "f3") (+ x 1)))
((multi-chain f1 f2 f3) 3)
((multi-compose f1 f2 f3) 3)
]}