dds/scribblings/functions.scrbl

311 lines
11 KiB
Racket

#lang scribble/manual
@(require scribble/example racket/sandbox
(for-label typed/racket/base "../functions.rkt" dds/utils
typed/racket/unsafe))
@title[#:tag "functions"]{dds/functions: Formal Functions}
@defmodule[dds/functions]
This modules provides some definitions for working with functions: tabulating,
(re)constructing from tables, generating random functions, etc.
Some definitions of particular kinds of functions are also provided (threshold
Boolean functions, etc.).
@(define functions-evaluator
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string]
[sandbox-memory-limit 50])
(make-evaluator 'typed/racket #:requires '((submod "functions.rkt" typed)))))
@section[#:tag "pseudovariadic"]{Pseudovariadic functions}
Functions for @seclink["tabulating"]{tabulating functions} take as an argument
a function to tabulate or a list of functions to tabulate. Writing the type of
such functions in Typed Racket and generalizing on the number of the arguments
is hard, and using functions with such types seems even harder.
The @seclink["tabulating"]{following section} contains some examples,
illustrating among other things the difficulties of typing
tabulating functions.
The type of @racket[apply] does not help in this situation, because Typed
Racket treats @racket[apply] in
@hyperlink["https://racket.discourse.group/t/replicating-the-type-of-apply/770/3"]{a
special way}. This means that a user-defined function with the same type as
@racket[apply] and directly calling it will not work in the same way.
@examples[#:eval functions-evaluator
apply
(define myapply apply)
myapply
(apply (λ (x y) (and x y)) '(#t #f))
(eval:error (myapply (λ (x y) (and x y)) '(#t #f)))
]
One way to work around this issue is to write functions which disguise as
variadic functions of type @racket[(-> a * b)], but which throw an exception
when they receive a number of arguments different from a given constant value.
Such functions are called @italic{pseudovariadic functions} in
this documentation.
@deftogether[(@defform[(pseudovariadic-lambda (id ...+) body ...+)]
@defform[(pvλ (id ...+) body ...+)])]{
Define a pseudovariadic anonymous function.
@examples[#:eval functions-evaluator
(: f (-> Boolean * Boolean))
(define f (pseudovariadic-lambda (x y) (and x y)))
(f #t #f)
(eval:error (f #t #f #t))
]}
@deftogether[(@defform[(pseudovariadic-define (name id ...+) body ...+)]
@defform[(pvdefine (id ...+) body ...+)])]{
Define a pseudovariadic function called @racket[name].
@examples[#:eval functions-evaluator
(: g (-> Boolean * Boolean))
(pseudovariadic-define (g x y) (and x y))
(g #t #f)
(eval:error (g #t #f #t))
]}
@section[#:tag "tabulating"]{Tabulating functions}
@defproc[(tabulate [func (-> a ... b)]
[doms (List (Listof a) ... a)])
(Listof (Listof (U Any b)))]{
Given a function @racket[func] and a list of domains @racket[doms] for each of
its arguments, in order, produces a list of lists giving the values of
arguments and the value of the functions for these inputs.
@examples[#:eval functions-evaluator
(tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
]}
@defproc[(tabulate/strict [func (-> a ... b)]
[doms (List (Listof a) ... a)])
(Listof (List (List a ...) (Listof b)))]{
Like @racket[tabulate], but the types of the arguments of @racket[func]
explicitly appear in the return type.
As of 2022-03-06, I am not able to write the type of a list first containing
elements of types @racket[a ...], followed by an element of type @racket[b].
This is why this function returns a list of lists, each containing first a list
of inputs, and then the output of @racket[func].
@examples[#:eval functions-evaluator
(tabulate/strict (λ (x y) (and x y)) '((#f #t) (#f #t)))
]}
@defproc[(tabulate/pv [func (-> a * b)]
[doms (Listof (Listof a))])
(Listof (Listof (U a b)))]{
Like @racket[tabulate], but @racket[func]
@seclink["pseudovariadic"]{pseudovariadic}.
@examples[#:eval functions-evaluator
(tabulate/pv (pvλ (x y) (and x y)) '((#f #t) (#f #t)))
]}
@defproc[(tabulate* [funcs (Listof (-> a ... b))]
[doms (List (Listof a) ... a)])
(Listof (Listof (U Any b)))]{
Like @racket[tabulate], but @racket[funcs] is a list of functions taking the
same arguments over the same domains.
@examples[#:eval functions-evaluator
(tabulate* (list (λ (x y) (and x y))
(λ (x y) (or x y)))
'((#f #t) (#f #t)))
]}
@defproc[(tabulate*/strict [funcs (Listof (-> a ... b))]
[doms (List (Listof a) ... a)])
(Listof (List (List a ...) (Listof b)))]{
Like @racket[tabulate*], but the types of the arguments of the functions
explicitly appear in the return type.
As of 2022-03-06, I am not able to write the type of a list first containing
elements of types @racket[a ...], followed by a list of elements of type
@racket[b]. This is why this function returns a list of lists, each containing
first a list of inputs, and then the list of outputs of @racket[funcs].
@examples[#:eval functions-evaluator
(tabulate*/strict (list (λ (x y) (and x y))
(λ (x y) (or x y)))
'((#f #t) (#f #t)))
]
The result of @racket[tabulate*] can be obtained by applying
@racket[append-lists]:
@examples[#:eval functions-evaluator
(require (only-in "utils.rkt" append-lists))
(append-lists (tabulate*/strict (list (λ (x y) (and x y))
(λ (x y) (or x y)))
'((#f #t) (#f #t))))
]}
@defproc[(tabulate*/pv [funcs (Listof (-> a * b))]
[doms (Listof (Listof a))])
(Listof (Listof (U a b)))]{
Like @racket[tabulate*], but the functions in @racket[funcs]
are @seclink["pseudovariadic"]{pseudovariadic}.
@examples[#:eval functions-evaluator
(tabulate*/pv (list (pvλ (x y) (and x y))
(pvλ (x y) (or x y)))
'((#f #t) (#f #t)))
]}
@defproc[(tabulate/pv/boolean [arity Integer] [func (-> Boolean * Boolean)])
(Listof (Listof Boolean))]{
Like @racket[tabulate/pv], but assumes the domains of all variables of the
function are Boolean. The arity of @racket[func] must be explicitly supplied.
@examples[#:eval functions-evaluator
(tabulate/pv/boolean 2 (pvλ (x y) (and x y)))
]
Explicitly supplying the arity is necessary because the actual arity of
a pseudovariadic function cannot be determined programmatically. Note that
@racket[tabulate] can be applied directly to a function, but the type of
@racket[tabulate] requires a cast is required the domains argument
@racket[doms].
@examples[#:eval functions-evaluator
(tabulate (λ (x y) (and x y))
(cast (make-list 2 '(#f #t))
(List (Listof Boolean) (Listof Boolean))))
]
This cast is what makes it necessary to resort to pseudovariadic functions and
explicit @racket[arity] to be able to write a type for
@racket[tabulate/pv/boolean].
See also @secref{fuctions/untyped} for simpler, but untyped version of
this function.
}
@defproc[(tabulate*/pv/boolean [arity Integer]
[func (Listof (-> Boolean * Boolean))])
(Listof (Listof Boolean))]{
Like @racket[tabulate/pv/boolean], but takes a list of functions of the
same arity.
@examples[#:eval functions-evaluator
(tabulate*/pv/boolean 2 (list (pvλ (x y) (and x y))
(pvλ (x y) (or x y))))
]}
@section{Constructing functions}
@section{Random functions}
@section{Threshold Boolean functions}
@section[#:tag "fuctions/untyped"]{Untyped definitions}
@defmodule[(submod dds/functions typed untyped)]
@(require (for-label (only-in racket/contract/base listof any/c)
(for-label (only-in (submod "../functions.rkt" typed untyped)
tabulate/boolean tabulate*/boolean
tabulate/01 tabulate*/01))))
This submodule contains some functions which cannot be typed or some functions
for which Typed Racket cannot produce contracts, i.e. polymorphic functions of
variable arity. The definitions in this submodule specifically target untyped
user code.
Since the names of some of the definitions in this submodule are the same in
the main module, and since they are imported in the same namespace for
rendering this document, some references to untyped definitions may wrongfully
point to typed definitions. As a tentative fix, all such references are
accompanied by the explicit mention "untyped".
@(define functions-evaluator/untyped
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string]
[sandbox-memory-limit 50])
(make-evaluator 'racket #:requires '((submod "functions.rkt" typed untyped)))))
@defproc[(tabulate [funcs procedure?]
[doms (listof list?)])
(listof list?)]{
Given a function @racket[func] and a list of domains @racket[doms] for each of
its arguments, in order, produces a list of lists giving the values of
arguments and the value of the functions for these inputs.
@examples[#:eval functions-evaluator/untyped
(tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
]}
@defproc[(tabulate* [funcs (listof procedure?)]
[doms (listof list?)])
(listof list?)]{
Like @racket[tabulate] (untyped), but @racket[funcs] is a list of functions
taking the same arguments over the same domains.
@examples[#:eval functions-evaluator/untyped
(tabulate* (list (λ (x y) (and x y))
(λ (x y) (or x y)))
'((#f #t) (#f #t)))
]}
@defproc[(tabulate/boolean [func procedure?]) (listof (listof boolean?))]{
Like @racket[tabulate] (untyped), but assumes the domains of all variables of
the function are Boolean. @racket[func] must have a fixed arity. It is an
error to supply a function of variable arity.
@examples[#:eval functions-evaluator/untyped
(tabulate/boolean (lambda (x y) (and x y)))
]}
@defproc[(tabulate*/boolean [funcs (non-empty-listof procedure?)])
(listof (listof boolean?))]{
Like @racket[tabulate/boolean], but takes a list of functions of the
same arity.
@examples[#:eval functions-evaluator/untyped
(tabulate*/boolean `(,(λ (x y) (and x y))
,(λ (x y) (or x y))))
]}
@defproc[(tabulate/01 [func procedure?]) (listof (listof (or/c 0 1)))]{
Like @racket[tabulate] (untyped), but assumes the domains of all variables of
the function are @tt{{0,1}}. @racket[func] must have a fixed arity. It is an
error to supply a function of variable arity.
@examples[#:eval functions-evaluator/untyped
(tabulate/01 (λ (x y) (modulo (+ x y) 2)))
]
The same remarks apply as for @racket[tabulate/boolean] (untyped).
}
@defproc[(tabulate*/01 [funcs (listof procedure?)]) (listof (listof (or/c 0 1)))]{
Like @racket[tabulate/01], but takes a list of functions of the same arity.
@examples[#:eval functions-evaluator/untyped
(tabulate*/01 `(,(λ (x y) (min x y)) ,(λ (x y) (max x y))))
]}