racket/collects/scribblings/htdp-langs/prim-ops.rkt

530 lines
18 KiB
Racket

#reader scribble/reader
#lang racket/base
(require "common.ss"
scribble/decode
scribble/struct
scribble/racket
racket/list
racket/pretty
syntax/docprovide
(for-syntax racket/base)
)
(provide prim-variables
prim-forms
define-forms/normal
define-form/explicit-lambda
intermediate-forms
prim-ops
prim-op-defns)
(define (maybe-make-table l t)
(if (paragraph? t)
(make-paragraph
(append l (cons " "
(paragraph-content t))))
(make-table
"prototype"
(list (list (make-flow (list (make-paragraph l)))
(make-flow (list t)))))))
(define (typeset-type type)
(let-values ([(in out) (make-pipe)])
(parameterize ([pretty-print-columns 50])
(pretty-print type out))
(port-count-lines! in)
(read-syntax #f in)))
(define (sort-category category)
(sort
(cadr category)
(lambda (x y)
(string<=? (symbol->string (car x))
(symbol->string (car y))))))
(define (make-proto func ctx-stx)
(maybe-make-table
(list
(hspace 2)
(to-element (datum->syntax ctx-stx (car func)))
(hspace 1)
":"
(hspace 1))
(to-paragraph
(typeset-type (cadr func)))))
(define-syntax-rule (prim-variables (section-prefix) empty true false)
(make-splice
(list
@section[#:tag (string-append section-prefix " Pre-Defined Variables")]{Pre-Defined Variables}
@defthing[empty empty?]{
The empty list.}
@defthing[true boolean?]{
The true value.}
@defthing[false boolean?]{
The false value.}
)))
(define-syntax-rule (define-forms/normal define)
(make-splice
(list
@defform*[[(define (... (name variable variable ...)) expression)]]{
Defines a function named @racket[name]. The @racket[expression] is the body
of the function. When the function is called,
the values of the arguments are inserted into the body in place of the
@racket[variable]s. The function returns the value of that new expression.
The function name's cannot be the same as that of another function or
variable.}
@defform/none[#:literals (define) (define name expression)]{
Defines a variable called @racket[name] with the the value of
@racket[expression]. The variable name's cannot be the same as that of
another function or variable, and @racket[name] itself must not appear in
@racket[expression].}
)))
(define-syntax-rule (define-form/explicit-lambda define lambda)
(make-splice
(list
@defform/none[#:literals (define lambda)
(... (define name (lambda (variable variable ...) expression)))]{
An alternate way on defining functions. The @racket[name] is the name of
the function, which cannot be the same as that of another function or
variable.
@defidform/inline[lambda] cannot be used outside of this alternate syntax.
}
)))
(define-syntax-rule (prim-forms
(section-prefix)
define
lambda
define-struct
define-wish
cond
else
if
and
or
check-expect
check-within
check-error
check-member-of
check-range
require)
(make-splice
(list
@; ----------------------------------------------------------------------
@defform*[[(... (define-struct structure-name (field-name ...)))]]{
Defines a new structure called @racket[field-name]. The structure's fields are
named by the @racket[field-name]s. After the @racket[define-struct], the following new
functions are available:
@itemize[
@item{@racketidfont{make-}@racket[structure-name] : takes in a number of
arguments equal to the number of fields in the structure,
and creates a new instance of that structure.}
@item{@racket[structure-name]@racketidfont{-}@racket[field-name] : takes in an
instance of the structure and returns the value in the field named by
@racket[field-name].}
@item{@racket[structure-name]@racketidfont{?} : takes in any value, and returns
@racket[true] if the value is an instance of the structure.}
]
The name of the new functions introduced by @racket[define-struct] must not be the same as that of other functions or
variables, otherwise @racket[define-struct] reports an error.}
#|
@defform*[[(define-wish name)]]{
Defines a function called @racket[name] that we wish exists but have not
implemented yet. The wished-for function can be called with one argument, and
are reported in the test report for the current program.
The name of the function cannot be the same as another function or variable.}
@defform/none[#:literals (define-wish)
(define-wish name expression)]{
Similar to the above form, defines a wished-for function named @racket[name]. If the
wished-for function is called with one value, it returns the values of @racket[expression]. }
|#
@; ----------------------------------------------------------------------
@defform*[[(... (name expression expression ...))]]{
Calls the function named @racket[name]. The value of the call is the value of
@racket[name]'s body when every one of the function's variables are
replaced by the values of the corresponding @racket[expression]s.
The function named @racket[name] must defined before it can be called. The
number of argument @racket[expression]s must be the same as the number of arguments
expected by the function.}
@; ----------------------------------------------------------------------
@defform*[#:literals (cond else)
[(... (cond [question-expression answer-expression] ...))
(... (cond [question-expression answer-expression] ... [else answer-expression]))]]{
Chooses a clause base on a condition. @racket[cond] finds the first
@racket[question-expression] which evaluates to @racket[true], then it evaluates
the corresponding @racket[answer-expression].
If none of the @racket[question-expression]s evaluates to @racket[true],
@racket[cond]'s value is the @racket[answer-expression] of the
@racket[else] clause. If there is no @racket[else], @racket[cond] reports
an error. If the result of a @racket[question-expression] is neither
@racket[true] nor @racket[false], @racket[cond] also reports an error.
@defidform/inline[else] cannot be used outside of @racket[cond].
}
@; ----------------------------------------------------------------------
@defform*[[(if test-expression then-expression else-expression)]]{
When the value of the @racket[test-expression] is @racket[true],
@racket[if] evaluates the @racket[then-expression]. When the test is
@racket[false], @racket[if] evaluates the @racket[else-expression].
If the @racket[test-expression] is neither @racket[true] nor
@racket[false], @racket[if] reports an error.}
@; ----------------------------------------------------------------------
@defform*[[(... (and expression expression expression ...))]]{
@racket[and] evaluates to @racket[true] if all the @racket[expression]s are
@racket[true]. If any @racket[expression] is false, the @racket[and]
expression immediately evaluates to @racket[false] (the expressions to the
right of that expression are not evaluated.)
If any of the expressions evaluate to a value other than @racket[true] or
@racket[false], it is an error.}
@; ----------------------------------------------------------------------
@defform*[[(... (or expression expression expression ...))]]{
@racket[or] evaluates to @racket[true] as soon as one of the
@racket[expression]s is @racket[true] (the expressions to the right of that
expression are not evaluated.) If all the @racket[expression] are false,
@racket[or] is @racket[false].
If any of the expressions evaluate to a value other than @racket[true] or
@racket[false], @racket[or] reports an error.}
@; ----------------------------------------------------------------------
@defform*[[(check-expect expression expected-expression)]]{
Checks that the first @racket[expression] evaluates to the same value as the
@racket[expected-expression].}
@defform*[[(check-within expression expected-expression delta-expression)]]{
Checks that the first @racket[expression] evaluates to a value within
@racket[delta-expression] of the @racket[expected-expression]. If
@racket[delta-expression] is not a number, @racket[check-within] report an
error.}
@defform*[[(check-error expression expression)
(check-error expression)]]{
Checks that the first @racket[expression] reports an error,
where the error messages matches the string produced by the second
@racket[expression], if it is present.}
@defform*[[(... (check-member-of expression expression expression ...))]]{
Checks that the first @racket[expression] produces the same value as one of
the following @racket[expression]s.}
@defform*[[(check-range expression expression expression)]]{
Checks that the first @racket[expression] produces a number in between the numbers
produced by the second and third @racket[expression]s, inclusive.}
@; ----------------------------------------------------------------------
@defform*[[(require string)]]{
Makes the definitions of the module specified by @racket[string]
available in the current module (i.e., the current file), where @racket[string]
refers to a file relative to the current file.
The @racket[string] is constrained in several ways to avoid problems
with different path conventions on different platforms: a @litchar{/}
is a directory separator, @litchar{.} always means the current
directory, @litchar{..} always means the parent directory, path
elements can use only @litchar{a} through @litchar{z} (uppercase or
lowercase), @litchar{0} through @litchar{9}, @litchar{-}, @litchar{_},
and @litchar{.}, and the string cannot be empty or contain a leading
or trailing @litchar{/}.}
@defform/none[#:literals (require)
(require module-name)]{
Accesses a file in an installed library. The library name is an identifier
with the same constraints as for a relative-path string (though without the
quotes), with the additional constraint that it must not contain a
@litchar{.}.}
@defform/none[#:literals (require lib)
(... (require (lib string string ...)))]{
Accesses a file in an installed library, making its definitions
available in the current module (i.e., the current file). The first
@racket[string] names the library file, and the remaining
@racket[string]s name the collection (and sub-collection, and so on)
where the file is installed. Each string is constrained in the same
way as for the @racket[(require string)] form.}
@defform/none[#:literals (require planet)
(require (planet string (string string number number)))]{
Accesses a library that is distributed on the internet via the PLaneT
server, making it definitions available in the current module (i.e.,
current file).}
)))
(define-syntax-rule
(intermediate-forms lambda
quote
quasiquote
unquote
unquote-splicing
local
letrec
let*
let
time)
(make-splice
(list
@deftogether[(
@defform/none[(unsyntax @elem{@racketvalfont{'}@racket[name]})]
@defform/none[(unsyntax @elem{@racketvalfont{'}@racket[part]})]
@defform[(quote name)]
@defform/none[(quote part)]
)]{
A quoted name is a symbol. A quote part is an abbreviation for a nested lists.
Normally, this quotation is written with a @litchar{'}, like
@racket['(apple banana)], but it can also be written with @racket[quote], like
@racket[(@#,racket[quote] (apple banana))].}
@deftogether[(
@defform/none[(unsyntax @elem{@racketvalfont{`}@racket[name]})]
@defform/none[(unsyntax @elem{@racketvalfont{`}@racket[part]})]
@defform[(quasiquote name)]
@defform/none[(quasiquote part)]
)]{
Like @racket[quote], but also allows escaping to expression ``unquotes.''
Normally, quasi-quotations are written with a backquote, @litchar{`}, like
@racket[`(apple ,(+ 1 2))], but they can also be written with
@racket[quasiquote], like
@racket[(@#,racket[quasiquote] (apple ,(+ 1 2)))].}
@deftogether[(
@defform/none[(unsyntax @elem{@racketvalfont{,}@racket[expression]})]
@defform[(unquote expression)]
)]{
Under a single quasiquote, @racketfont{,}@racket[expression] escapes from
the quote to include an evaluated expression whose result is inserted
into the abbreviated list.
Under multiple quasiquotes, @racketfont{,}@racket[expression] is really
the literal @racketfont{,}@racket[expression], decrementing the quasiquote count
by one for @racket[expression].
Normally, an unquote is written with @litchar{,}, but it can also be
written with @racket[unquote].}
@deftogether[(
@defform/none[(unsyntax @elem{@racketvalfont[",@"]@racket[expression]})]
@defform[(unquote-splicing expression)]
)]{
Under a single quasiquote, @racketfont[",@"]@racket[expression] escapes from
the quote to include an evaluated expression whose result is a list to
splice into the abbreviated list.
Under multiple quasiquotes, a splicing unquote is like an unquote;
that is, it decrements the quasiquote count by one.
Normally, a splicing unquote is written with @litchar{,}, but it can
also be written with @racket[unquote-splicing].}
@defform[(... (local [definition ...] expression))]{
Groups related definitions for use in @racket[expression]. Each
@racket[definition] can be either a variable definition, a function
definition, or a structure definition, using the usual syntax.
When evaluating @racket[local], each @racket[definition] is evaluated in
order, and finally the body @racket[expression] is evaluated. Only the
expressions within the @racket[local] (including the right-hand-sides of
the @racket[definition]s and the @racket[expression]) may refer to the
names defined by the @racket[definition]s. If a name defined in the
@racket[local] is the same as a top-level binding, the inner one
``shadows'' the outer one. That is, inside the @racket[local], any
references to that name refer to the inner one.}
@; ----------------------------------------------------------------------
@defform[(... (letrec ([name expr-for-let] ...) expression))]{
Like @racket[local], but with a simpler syntax. Each @racket[name] defines
a variables (or a functions) with the value of the corresponding
@racket[expr-for-let]. If @racket[expr-for-let] is a @racket[lambda],
@racket[letrec] defines a function, otherwise it defines a variable.}
@defform[(... (let* ([name expr-for-let] ...) expression))]{
Like @racket[letrec], but each @racket[name] can only be used in
@racket[expression], and in @racket[expr-for-let]s occuring after that
@racket[name].}
@defform[(... (let ([name expr-for-let] ...) expression))]{
Like @racket[letrec], but the defined @racket[name]s can be used only in
the last @racket[expression], not the @racket[expr-for-let]s next to the
@racket[name]s.}
@; ----------------------------------------------------------------------
@defform[(time expression)]{
Measures the time taken to evaluate @racket[expression]. After evaluating
@racket[expression], @racket[time] prints out the time taken by the
evaluation (including real time, time taken by the cpu, and the time spent
collecting free memory). The value of @racket[time] is the same as that of @racket[expression].}
)))
(define (prim-ops lib ctx-stx)
(let ([ops (map (lambda (cat)
(cons (car cat)
(list (cdr cat))))
(lookup-documentation lib 'procedures))])
(make-table
#f
(cons
(list
(make-flow
(list
(make-paragraph
(list "In function calls, the function appearing immediatly after the open parenthesis can be any functions
defined with " (racket define) " or " (racket define-struct) ", or any one of:")))))
(apply
append
(map (lambda (category)
(cons
(list (make-flow
(list
(make-paragraph (list (hspace 1)
(bold (car category)))))))
(map (lambda (func)
(list
(make-flow
(list
(make-proto func ctx-stx)))))
(sort-category category))))
ops))))))
(define (prim-op-defns lib ctx-stx not-in)
(make-splice
(let ([ops (map (lambda (cat)
(cons (car cat)
(list (cdr cat))))
(lookup-documentation lib 'procedures))]
[not-in-ns (map (lambda (not-in-mod)
(let ([ns (make-base-namespace)])
(parameterize ([current-namespace ns])
(namespace-require `(for-label ,not-in-mod)))
ns))
not-in)])
(apply
append
(map (lambda (category)
(cons
(subsection #:tag-prefix (format "~a" lib) (car category))
(filter values
(map
(lambda (func)
(let ([id (datum->syntax ctx-stx (car func))])
(and (not (ormap
(lambda (ns)
(free-label-identifier=?
id
(parameterize ([current-namespace ns])
(namespace-syntax-introduce (datum->syntax #f (car func))))))
not-in-ns))
(let ([desc-strs (cddr func)])
(defthing/proc
id
(to-paragraph (typeset-type (cadr func)))
desc-strs)))))
(sort-category category)))))
ops)))))