2020-11-29 17:43:20 +01:00
|
|
|
#lang scribble/manual
|
2022-03-05 21:37:24 +01:00
|
|
|
@(require scribble/example racket/sandbox
|
2022-03-06 22:53:33 +01:00
|
|
|
(for-label typed/racket/base "../functions.rkt" dds/utils
|
|
|
|
typed/racket/unsafe))
|
2020-11-29 17:43:20 +01:00
|
|
|
|
|
|
|
@title[#:tag "functions"]{dds/functions: Formal Functions}
|
|
|
|
|
2020-11-29 21:45:33 +01:00
|
|
|
@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.).
|
|
|
|
|
2022-03-05 21:37:24 +01:00
|
|
|
@(define functions-evaluator
|
|
|
|
(parameterize ([sandbox-output 'string]
|
|
|
|
[sandbox-error-output 'string]
|
|
|
|
[sandbox-memory-limit 50])
|
|
|
|
(make-evaluator 'typed/racket #:requires '((submod "functions.rkt" typed)))))
|
|
|
|
|
2022-03-21 00:04:21 +01:00
|
|
|
@section[#:tag "pseudovariadic"]{Pseudovariadic functions}
|
2022-03-20 19:34:48 +01:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2022-03-20 20:42:29 +01:00
|
|
|
@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))
|
|
|
|
]}
|
2022-03-20 19:34:48 +01:00
|
|
|
@section[#:tag "tabulating"]{Tabulating functions}
|
2020-11-29 21:45:33 +01:00
|
|
|
|
2022-03-06 23:39:51 +01:00
|
|
|
@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)))
|
|
|
|
]}
|
|
|
|
|
2022-03-21 00:04:21 +01:00
|
|
|
@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)))
|
|
|
|
]}
|
|
|
|
|
2022-03-06 19:54:05 +01:00
|
|
|
@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))))
|
|
|
|
]}
|
|
|
|
|
2022-03-21 00:04:21 +01:00
|
|
|
@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)))
|
|
|
|
]}
|
|
|
|
|
2022-04-09 02:03:51 +02:00
|
|
|
@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))))
|
|
|
|
]}
|
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@section{Constructing functions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@section{Random functions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@section{Threshold Boolean functions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@section[#:tag "fuctions/untyped"]{Untyped definitions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@defmodule[(submod dds/functions untyped)]
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@(require (for-label (only-in racket/contract/base listof any/c)))
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
@(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)))
|
2022-03-06 22:53:33 +01:00
|
|
|
]}
|
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@defproc[(tabulate* [funcs (listof procedure?)]
|
|
|
|
[doms (listof list?)])
|
|
|
|
(listof list?)]{
|
2020-11-29 21:45:33 +01:00
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
Like @racket[tabulate], but @racket[funcs] is a list of functions taking the
|
|
|
|
same arguments over the same domains.
|
2020-11-29 21:45:33 +01:00
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@examples[#:eval functions-evaluator/untyped
|
|
|
|
(tabulate* (list (λ (x y) (and x y))
|
|
|
|
(λ (x y) (or x y)))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
]}
|
2022-04-09 01:32:54 +02:00
|
|
|
|
|
|
|
@defproc[(tabulate/boolean [func procedure?]) (listof (listof boolean?))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate], 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))))
|
|
|
|
]}
|