Sketchy LISP Reference |
Copyright (C) 2006 Nils M Holm |
[<<Reduction Rules] | [Contents] [Index] | [Pre-Defined Symbols>>] |
4.1 Introduction
4.2 Bindings and Definitions
4.3 Control
4.4 Composition and Decomposition
4.5 Predicates
4.6 Type Conversion
4.7 Char Functions
4.8 Numeric Functions
4.9 String Functions
4.10 Input/Output
Primitive functions are those functions that cannot be expressed in terms of Sketchy in a practical way. These functions form the core of the language. Some of the primitives listed in this chapter are in fact pseudo functions. Their applications look like function applications, but their semantics differ from regular function applications in some subtle details, which will be outlined in this chapter. Most pseudo functions are called by name.
In this section a symbol is called a symbol only if it is
quoted. Otherwise it is called a variable. Eg, 'x
and
=> x
denote symbols and x
denotes a
variable.
At the beginning of each function description, a generalized sample application is given. This application lists the types that the function expects by giving examples:
(car '(x . y)) => x
Unless such an example uses a variable as argument, it is an error to apply the function to a type other than that specified in the example:
(car 'non-pair) => bottom
When a variable is used as an argument, the function accepts any type:
(pair? x) => {#t,#f}
The symbol bottom
is used to denote an
undefined
value. A value is
undefined, if it does not have
a unique normal form. This does not imply that
a reduction that results in bottom
terminates
with an error. It rather says that the result of the reduction
should be considered invalid.
As an example, assume that both the symbols x and y are bound to the numeric value 457. Given these bindings, the expression
(eq? x y)
may reduce to #t
or #f
,
since the identity of instances of numbers is arguable. Because the
result is not predictable, it must be considered undefined.
(bottom ...) => bottom
(Bottom)
evaluates to an undefined result.
Any expression that contains a subexpression evaluating to
(bottom)
evaluates itself to
(bottom)
. The bottom
function
may have any number of arguments.
(bottom) => bottom (bottom 'x 'y 'z) => bottom (eq? (bottom) ()) => bottom
The bottom
function is specific to Sketchy.
(define x y) => #<void>
Define
introduces the given symbol x and
then binds the normal form of y to that symbol:
(define x y) => #<void> ; x := eval[y]
The notation x := y denotes that the value
of y is bound to x globally
.
A binding is global, if it is
visible in an entire program.
Since its arguments are passed to define
call-by-name, it does not matter what x is bound to
when define
is applied. The previous binding of
x is lost.
Define
is used in association with
lambda
to create new functions:
(define f (lambda (x) t))
Functions created using define
may be mutually
recursive (see letrec
).
In addition to the above notation for defining function, Sketchy also supports the R5RS notation:
(define (f x) t) = (define f (lambda (x) t)) (define (f x . y) t) = (define f (lambda (x . y) t)) (define (f . x) t) = (define f (lambda x t))
Applications of define
may not occur inside of other
expressions.
The define
pseudo function conforms mostly to R5RS.
It differs from R5RS in this points:
define
may be applied at the top level only.(lambda (x1 ... xN) t) => #<closure (x1 ... xN)>
Applications of lambda
reduce to
lexical closures.
If the term t of a lambda expression
(lambda (x) t)
contains any free variables, an association list (a list of key/value pairs) is added to the resulting closure. For example,
(letrec ((y 'foo)) (lambda (x) (cons y x))) => #<closure (x) (cons y x) ((y . foo))>
(The function term and the environment normally do not print
in closures, but the :closure-form 2
meta command can be
used to make them visible.)
The association list '((y . foo))
is called the
lexical environment of the
closure. When a closure is applied to a value, its free variables
get bound to the values stored in the environment:
((lambda (x) (cons y x) ((y . foo))) 'bar) => '(foo . bar)
The lambda
pseudo function mostly conforms to R5RS.
It differs from R5RS in the following point:
lambda
accepts an optional
alist forming a lexical environment as its third argument.(let ((x1 v1) ... (xN vN)) z) => eval[z]
Let
binds each symbol xI to
eval[vI], thereby forming a local environment. The
expression z is evaluated inside of that local
environment. The normal form of z is the normal form
of the entire let
expression.
Let
first evaluates all values
v1..vN and then binds their normal forms to the
symbols x1..xN. Therefore, symbols in the values
of let
are either unbound or bound in the outer context
of that let
:
(let ((x 'outer)) (let ((x 'inner) (y x)) y)) => outer
Except for its syntax, the let
construct is perfectly
equal to the application of a lambda function:
(let ((x 3) (y 4)) = ((lambda (x y) (* x y)) (* x y)) 3 4)
The let
pseudo function conforms to R5RS.
(letrec ((x1 v1) ... (xN vN)) z) => eval[z]
Letrec
binds each symbol xI to
eval[vI], thereby forming a local environment. The
expression z is evaluated inside of that local
environment. The normal form of z is the normal form
of the entire letrec
expression.
Letrec
is basically equal to let
, but after
binding its values to its arguments, it fixes recursive bindings using
recursive-bind
. Hence it can be used to bind recursive or
even mutually recursive functions:
(letrec ((even-p (lambda (x) (cond ((null? x) #t) (#t (odd-p (cdr x)))))) (odd-p (lambda (x) (cond ((null? x) #f) (#t (even-p (cdr x))))))) (list (odd-p '(i i i)) (even-p '(i i i)))) => (#t #f)
It is an error for a binding of letrec
to refer
to the value of another binding of the same
letrec
:
(letrec ((foo 'outer-value)) (letrec ((foo 'inner-value) (bar foo)) bar)) => bottom
The letrec
pseudo function conforms to R5RS.
(package ['x]]) => x
The package
pseudo function is used to protect
global symbols from re-definition. The symbols to be protected
(which are typically created using define
) are enclosed
in two applications of package
. The first application
creates a new package which is to contain the protected symbols.
The second one closes the package. An opening application names the
package using its argument. Closing applications have no arguments.
When multiple packages define equal symbols, the currently open package is always searched first. The order of searching the other packages is unspecified. When no user-defined package is open, an initial package called the default package is open.
The following program demonstrates the use of package
:
(package 'foo) ; create and open package foo (define bar 'baz) (define (f) bar) (package) ; close package foo (f) => baz (define bar 'goo) ; re-define bar bar => goo ; bar is re-defined as expected (f) => baz ; f of foo still returns bar of foo
Caveats
A symbol is assigned to the currently open package when the symbol occurs for the first time and not when it is assigned a value. Hence the following example causes trouble:
f => bottom ; f is undefined (package 'foo) (define (f) 'bar) (package) (f) => bottom ; f is still undefined
F is created in the default package when it is read by the interpreter for the first time. Later another instance of f is created in the package foo. When closing foo, the f of the default package shadows the new definition, because the default package is searched first. Hence the f of foo becomes invisible.
Thers is normally no need to use package
in user-level
code. It was added to protect some internal functions of the Sketchy
implementation from re-definition.
The package
pseudo function is specific to Sketchy.
(quote x) => x
Quote
evaluates to its single argument. Because
arguments to quote
are passed to it call-by-name,
(quote x)
always results in 'x
while x itself would reduce to eval[x].
Quote
is used to quote expressions which are not to
be evaluated:
(()) => bottom (quote (())) => (()) (car '(x . y)) => x (quote (car '(x . y))) => (car '(x . y))
The notation 'x
is equal to
(quote x)
:
'(x y) = (quote (x y)) => (x y)
The quote
pseudo function conforms to R5RS.
(recursive-bind env) => env
The recursive-bind
function takes an environment having
the form of a association list (alist) as its argument and fixes up all
recursive references in that alist. It evaluates to the given environment
with all recursive references resolved.
Explanation
The alist may contain a binding to a recursive zero-argument lambda function f. Because applicative LISP cannot create cyclic stuctures, the environment of f binds f to an unspecific value:
((f . (lambda () (f) ((f . #<void>)))))
Therefore any use of f would result in an application of a non-function:
(f) -> ((lambda () (f) ((f . #<void>)))) -> (#<void>) => bottom
To make the recursive function f work, the binding of
f in the lexical environment of f must
be bound to the value of f in the outer environment
- the environment passed to recursive-bind
. In other
words: the local definition of f must bind to f
itself. This is exactly what recursive-bind
does:
(recursive-bind '((f . (lambda () (f) ((f . (void))))))) => ((f . (lambda () (f) ((f . (lambda () (f) ((f . ...))))))))
Recursive-bind
is capable of resolving mutually
recursive defintions as well.
Notes
The structure created by recursive-bind
is potentially
self-referential and hence infinite. Do not try to print it.
The creation of recursive bindings is normally done using
letrec
. Recursive-bind
is a conceptual
function that serves no other purpose than facilitating the
implementation of metacircular interpreters.
The recursive-bind
function is specific to Sketchy.
(void) => #<void>
The void
function evaluates to an invalid value.
By binding a symbol to (void)
, the binding of that
symbols becomes unspecific. That is, the symbol becomes
unbound
:
(define x 'foo) x => foo (define x (void)) x => bottom
The void
function is specific to Sketchy.
(and expr...) => expr#f
And
evaluates the given expressions from the left
to the right and returns the value of the first expression reducing
to #f
. When an expression reduces to logical falsity,
and
returns immediately and does not evaluate any
further expressions. When no argument of and
evaluates to #f
, the value of the application of
and
is equal to the value of the last expression.
The following rules apply:
(and) => #t (and 'foo) => foo (and #f) => #f (and #t 'foo) => foo (and #t #f) => #f (and #f 'foo) => #f (and #f #f) => #f (and 'foo 'bar) => bar
The and
pseudo function conforms to R5RS.
(apply fun a1 ... aN list) => eval[(fun a1 ... aN . list)]
Apply
applies the function fun to the
arguments contained in the list list. The additional
arguments a1...aN are optional. Both fun
and list are passed to apply
call-by-value,
but fun is applied to the arguments in list
call-by-name. The number of arguments of fun must match
the number of members of list. For example,
(apply cons '(a b)) => (a . b) (apply (lambda () 'foo) '()) => foo (apply cons '(a)) => bottom
If any additional arguments a1...aN are specified, these are consed to the argument list list before applying fun to it:
(apply cons 1 '(2)) => (1 . 2) (apply list 1 2 3 '(4 5)) => (1 2 3 4 5)
The apply
function conforms to R5RS.
Note:
Sketchy's apply
may be applied to special form handlers
such as and
, define
, etc, which R5RS
does not allow. If you want the R5RS behaviour, use the
strict-apply
meta command.
(begin expr1 ... exprN) => eval[exprN]
Begin
reduces each of the given expressions to their normal
form. Evaluation takes place in the order of occurrence. The normal form of
each but the last expression is discarded. The normal form of an application
of begin
is the normal form of its last argument.
The only effect of begin
is to make sure that the given
expressions are reduced in the given order. In fact,
(begin x1 x2 x3)
is just a shorter and more readable form of
((lambda (x) x3) ((lambda (x) x2) ((lambda () x1))))
Because begin
discards most of its results, it is
mostly used to group expressions with side effects (see input/output
primitives).
(begin) => #<void> (begin 'foo) => foo (begin 'a 'b 'c) => c
The begin
function conforms to R5RS.
(call/cc fun) => expr
(Call/cc fun)
captures the current continuation
and passes it to fun. Fun must be a function
of one argument. The following conversion takes place:
(call/cc fun) => (fun #<continuation>)
The current continuation
(represented by #<continuation>
) is the part of
an expression that will be evaluated next. For example, in
(cons 'foo (call/cc (lambda (k) 'bar)))
the (current) continuation of
(call/cc (lambda (k) 'bar))
is
(cons 'foo _)
where _ denotes the not-yet-evaluated application of
call/cc
.
Applications of call/cc
reduce to the result of
the function passed to call/cc
unless this function
applies its argument. For instance:
(call/cc (lambda (ignored) 'foo)) => foo
If the function passed to call/cc
applies the
captured continuation passed to it, though, the current context of the
formula is discarded and replaced with the captured continuation. The
application of call/cc
in the restored context
reduces to the argument of the continuation:
(cons 'foo (call/cc (lambda (k) (k 'bar)))) -> (cons 'foo ((lambda (k) (k 'bar)) #<continuation>)) -> (cons 'foo (#<continuation> 'bar)) -> (cons 'foo 'bar)
The current continuation of the application of a captured continuation is discarded, so:
(cons 'foo (call/cc (lambda (k) (cons 'zzz (k 'bar))))) -> (cons 'foo ((lambda (k) (cons 'zzz (k 'bar))) #<continuation>)) -> (cons 'foo (cons 'zzz (#<continuation> 'bar))) -> (cons 'foo 'bar)
Because activating the continuation k
replaces the current context with the one previously captured
by call/cc
, the
(cons 'foo (cons 'zzz _))
part is thrown away and replaced with
(cons 'foo _)
Finally, the application of call/cc
is replaced
with the value passed to k.
Because call/cc
can be used to expose the
order of evaluation
of function arguments, the reduction of lists - as explained
in the previous chapter - is extended as follows:
Members of lists are evaluated from the left to the right, so each Ai is guaranteed to be evaluated before Aj, if i<j in
(a1 ... aN)
Consequently,
(call/cc (lambda (k) (#f (k 'foo) (k 'bar)))) => foo
Note that this extension applies to Sketchy, but not to Scheme.
Once captured, continuations have
indefinite extent.
As long
as they can be referred to using a symbol, they remain valid.
Therefore they can be applied any number of times and even after
returning from call/cc
:
(letrec ((x (call/cc (lambda (k) (cons 'foo k))))) (let ((v (car x)) (k (cdr x))) (cond ((eq? v 'foo) (k (cons 'bar k))) ((eq? v 'bar) (k (cons 'baz k))) (#t v)))) => baz
The call/cc
function conforms to R5RS.
(cond (p1 e1) ... (pN eN)) => e#T
The arguments of cond
are passed to it
call-by-name. Each argument of cond
must be
a clause of the form
(Pj Ej)
where Pj is interpreted as a truth value.
Cond
first evaluates P1. If it
reduces to a true
value (something other than #f
),
cond
evaluates E1. In this case, E1
is the normal form of the entire cond
expression.
Otherwise cond
does not evaluate E1 and
proceeds with the following arguments until a clause with
Pj=/=#f
is found. The following
rules apply:
(cond (x y) (x2 y2)) => eval[y], if eval[x]=/=#f (cond (x y) (x2 y2)) => (cond (x2 y2)), if eval[x]=#f (cond (x y)) => bottom, if eval[x]=#f
The cond
pseudo function mostly conforms to R5RS.
It differs from the R5RS version in this point:
cond
is allowed to run out of clauses,
returning an unspecific value.(or expr...) => expr#t
Or
evaluates the given expressions from the left
to the right and returns the value of the first expression reducing
to logical truth (a value other than #f
). When an
expression reduces to logical truth, or
returns
immediately and does not evaluate any further expressions. When no
argument of or
evaluates to truth, the value of the
application of or
is equal to the value of the last
expression. The following rules apply:
(or) => #f (or 'foo) => foo (or #f) => #f (or #t 'foo) => #t (or #t #f) => #t (or #f 'foo) => foo (or #f #f) => #f (or 'foo 'bar) => foo
The or
pseudo function conforms to R5RS.
(car '(x . y)) => x
(Car x)
evaluates to the car part of
x. X must be a pair. The following
rules apply:
(car '(x . y)) => x (car '(x y)) => x (car '(x)) => x (car ()) => bottom (car 'non-pair) => bottom
The car
function conforms to R5RS.
(cdr '(x . y)) => y
(Cdr x)
evaluates to the cdr part of
x. X must be a pair. The following
rules apply:
(cdr '(x . y)) => y (cdr '(x y)) => (y) (cdr '(x)) => () (cdr ()) => bottom (cdr 'non-pair) => bottom
The cdr
function conforms to R5RS.
(cons x y) => (x.y)
Cons
creates a new pair from two given expressions.
Its first argument x forms the car part of the new pair
and its second argument y forms the cdr part. The
following rules apply:
(cons 'x 'y) => (x . y) (cons 'x ()) => (x) (cons 'x '(y . ())) => (x y) (cons 'x '(y)) => (x y) (cons 'x '(y . z)) => (x y . z)
Note: '(x y . z)
is a called an improper list
or a dotted list
, because its last element is not equal to
()
:
(cdr (cdr '(x y . z))) =/= '()
The cons
function conforms to R5RS.
(char? x) => {#t,#f}
(Char? x)
evaluates to #t
if x is a char literal and otherwise to
#f
. The following rules apply:
(char? #\x) => #t (char? #\space) => #t (char? #\\) => #t (char? 'non-char) => #f
The char?
function conforms to R5RS.
(eq? x y) => {#t,#f}
(Eq? x y)
evaluates to #t
if
x and y are identical and otherwise
to #f
. Two expressions are identical, if, and only if
they are the same symbol or they are both the same boolean literal or
they are both empty lists. All other atoms as well as pairs may be
different, even if they look equal. Objects bound to the same symbol are
always identical, so
(eq? a a) => #t
even if a is a variable. The following rules apply:
(eq? x x) => #t (eq? 'x 'x) => #t (eq? 'x 'y) => #f (eq? () ()) => #t (eq? 'x '(x . y)) => #f (eq? #f #f) => #t (eq? '(x . y) '(x . y)) => bottom (eq? '(x y) '(x y)) => bottom (eq? 123 123) => bottom (eq? #\x #\x) => bottom (eq? "foo" "foo") => bottom
The eq?
function conforms to R5RS.
(null? x) => {#t,#f}
(Null? x)
evaluates to #t
if x is equal to ()
and otherwise
to #f
. It easily may be defined using the
Sketchy function
(define (null? x) (eq? x ()))
but for reasons of efficiency, it has been implemented as a primitive function.
The null?
function conforms to R5RS.
(number? x) => {#t,#f}
(Number? x)
evaluates to #t
if x is a numeric literal and otherwise to
#f
. The following rules apply:
(number? 123) => #t (number? -123) => #t (number? +123) => #t (number? 'non-integer) => #f
The number?
function conforms to R5RS.
(pair? x) => {#t,#f}
Applications of pair?
evaluate to
#t
if x is a pair and otherwise to
#f
. The following rules apply:
(pair? '(x . y)) => #t (pair? '(x y)) => #t (pair? ()) => #f (pair? 'non-pair) => #f
The pair?
function conforms to R5RS.
(procedure? x) => {#t,#f}
(Procedure? x)
evaluates to #t
if x is a procedure and otherwise to #f
.
The following objects are procedures:
lambda
call/cc
The following rules apply:
(procedure? cons) => #t (procedure? procedure?) => #t (procedure? lambda) => #f (procedure? (lambda (x) x)) => #t (procedure? (call/cc (lambda (k) k))) => #t (procedure? 'non-procedure) => #f
The procedure?
function conforms to R5RS.
(string? x) => {#t,#f}
(String? x)
evaluates to #t
if x is a string literal and otherwise to
#f
. The following rules apply:
(string? "some text") => #t (string? "") => #t (string? 'non-string) => #f
The string?
function conforms to R5RS.
(symbol? x) => {#t,#f}
(Symbol? x)
evaluates to #t
if x is a literal symbol and otherwise to
#f
. The following rules apply:
(symbol? 'foo) => #t (symbol? 'symbol?) => #t (symbol? symbol?) => #f (symbol? "non-symbol") => #f
The symbol?
function conforms to R5RS.
(char->integer #\c) => integer
The char->integer
function evaluates to an integer
whose value is equal to the ASCII code of its argument.
The following rules apply:
(char->integer #\a) => 97 (char->integer #\A) => 65 (char->integer #\space) => 32 (char->integer #\\) => 92 (char->integer 'non-char) => bottom
The char->integer
function conforms to R5RS.
(integer->char 123) => char
The integer->char
function evaluates to a char
literal whose ASCII code is equal to its argument. The argument must
be in the range 0..127. The following rules apply:
(integer->char 97) => #\a (integer->char 65) => #\A (integer->char 32) => #\space (integer->char 92) => #\\ (integer->char 'non-integer) => bottom (integer->char -1) => bottom (integer->char 128) => bottom
The integer->char
function conforms to R5RS.
(integer->list 123) => list
The integer->list
function evaluates to a list
containing the digits and, if one exists, the prefix of the given
integer. Digits are represented by the symbols
0d
through 9d
.
The following rules apply:
(integer->list 0) => (0d) (integer->list 123) => (1d 2d 3d) (integer->list -5) => (- 5d) (integer->list +7) => (+ 7d) (integer->list 'non-integer) => bottom
The integer->list
function is specific to Sketchy.
(list->integer list) => integer
The list->integer
function evaluates to an
integer composed of the digits and the prefix (if any) contained
in the given list. The list must contain a positive number of digits,
and it may contain a sign of the form +
or -
at its first position. Digits are represented by the symbols
0d
through 9d
.
The following rules apply:
(list->integer '(0d)) => 0 (list->integer '(1d 2d 3d)) => 123 (list->integer '(- 7d)) => -7 (list->integer '(+ 5d)) => +5 (list->integer '(non-digit)) => bottom (list->integer '()) => bottom (list->integer '(-)) => bottom (list->integer '(+ + 1d)) => bottom (list->integer 'non-list) => bottom
The list->integer
function is specific to Sketchy.
(list->string list) => string
The list->string
function evaluates to a string
literal that is composed of the characters contained in the list
passed to it. The list must contain objects of the type char
exclusively. The following rules apply:
(list->string '(#\X)) => "X" (list->string '(#\t #\e #\x #\t)) => "text" (list->string '()) => "" (list->string '(#\")) => "\"" (list->string '(non-char)) => bottom (list->string 'non-list) => bottom
The list->string
function conforms to R5RS.
(string->list "xyz") => list
The string->list
function evaluates to a list
containing the same characters as the string passed to it. Each
character of the string will be represented by an individual char
literal in the resulting list. The following rules apply:
(string->list "X") => (#\X) (string->list "text") => (#\t #\e #\x #\t) (string->list "") => () (string->list "\"") => (#\") (string->list 'non-string) => bottom
The string->list
function conforms to R5RS.
(string->symbol "xyz") => xyz
The string->symbol
function evaluates to a symbol
that is composed of the characters of the given string argument.
The following rules apply:
(string->symbol "foo") => foo (string->symbol "FOO") => FOO (string->symbol " ") => (string->symbol "") => bottom (string->symbol 'non-string) => bottom
Notes
(1) String->symbol
may be used to create symbols that
cannot be accessed by programs, such as symbols containing white space,
symbols containing upper case letters, and symbols containing special
characters like parentheses, dots, semicolons, etc.
(2) Symbols containing spaces lead to an ambiguity, as demonstrated below. Therefore future implementation of Sketchy may refuse to create such symbols.
(string->symbol "foo bar") => foo bar (symbol->string (string->symbol "foo bar")) => "foo bar" (symbol->string 'foo bar) => bottom
The string->symbol
function mostly conforms to R5RS.
The R5RS variant allows the creation of empty symbols.
(symbol->string 'xyz) => "xyz"
The symbol->string
function evaluates to a string
that is composed of the characters of the literal symbol passed to it.
The following rules apply:
(symbol->string 'foo) => "foo" (symbol->string 'FOO) => "foo" (symbol->string "non-symbol") => bottom
The symbol->string
function conforms to R5RS.
(char-ci<? #\a #\B) => #t
The char-ci<?
function compares two chars and
returns #t
if the first of the two chars comes first
in the ASCII character set. Otherwise it returns #f
.
When comparing letters, their case is ignored. The following rules
apply:
(char-ci<? #\a #\b) => #t (char-ci<? #\a #\B) => #t (char-ci<? #\A #\b) => #t (char-ci<? #\A #\B) => #t (char-ci<? #\b #\a) => #f (char-ci<? 'non-char #\x) => bottom (char-ci<? #\x 'non-char) => bottom
The char-ci<?
function conforms to R5RS.
(char-ci=? #\C #\c) => #t
The char-ci=?
function compares two chars and
returns #t
if the two chars are equal. Otherwise
it returns #f
. When comparing letters, their case is
ignored. The following rules apply:
(char-ci=? #\a #\a) => #t (char-ci=? #\a #\A) => #t (char-ci=? #\A #\a) => #t (char-ci=? #\A #\A) => #t (char-ci=? #\a #\b) => #f (char-ci=? 'non-char #\x) => bottom (char-ci=? #\x 'non-char) => bottom
The char-ci=?
function conforms to R5RS.
(char<? #\a #\b) => #t
The char<?
function compares two chars and
returns #t
if the first of the two chars comes first
in the ASCII character set. Otherwise it returns #f
.
The following rules apply:
(char<? #\a #\b) => #t (char<? #\A #\B) => #t (char<? #\a #\B) => #f (char<? #\b #\a) => #f (char<? 'non-char #\x) => bottom (char<? #\x 'non-char) => bottom
The char<?
function conforms to R5RS.
(char=? #\c #\c) => #t
The char=?
function compares two chars and
returns #t
if the two chars are equal. Otherwise
it returns #f
. The following rules apply:
(char=? #\a #\a) => #t (char=? #\A #\A) => #t (char=? #\a #\A) => #f (char=? #\a #\b) => #f (char=? 'non-char #\x) => bottom (char=? #\x 'non-char) => bottom
The char=?
function conforms to R5RS.
(n+ num1 num2) => num3
The n+
function implements a multi-digit decimal full
adder. It adds two natural numbers num1 and
num2 (no signs are allowed!) and returns their sum
num3. Precision is arbitrary, no overflow can occur.
N+
is used to implement the more general bignum
arithmetic function like +
, *
, etc.
The following rules apply:
(n+ 12 9) => 23 (n+ 12 0) => 12 (n+ 12 +1) => bottom (n+ -12 1) => bottom (n+ 'non-number 123) => bottom (n+ 123 'non-number) => bottom
The n+
function is specific to Sketchy.
(n- num1 num2) => num3
The n-
function implements a multi-digit decimal
subtractor. It subtracts the number num2 from
num1, returning their difference num3.
Both of its arguments must be natural numbers without
any signs. N-
cannot create negative results, so
num2 must be less than or equal to num1.
This function may subtract number of any size.
N-
is used to implement the more general bignum
arithmetic function like +
, *
, etc.
The following rules apply:
(n- 12 9) => 3 (n- 12 0) => 12 (n- 9 12) => bottom (n- 12 +1) => bottom (n- -12 1) => bottom (n- 'non-number 123) => bottom (n- 123 'non-number) => bottom
The n-
function is specific to Sketchy.
(n< num1 num2) => {#t,#f}
The n<
function implements a multi-digit decimal
comparator. It compares the two natural numbers num1
and num2, returning #t
if num1 is
less than num2. In case num1 is greater than or
equal to num2, it returns #f
.
N<
may compare number of any size.
The n<
function is used to implement the more general
bignum comparison function like <
, >=
, etc.
The following rules apply:
(n< 9 12) => #t (n< 12 9) => #f (n< 12 +9) => bottom (n< -12 9) => bottom (n< 'non-number 123) => bottom (n< 123 'non-number) => bottom
The n<
function is specific to Sketchy.
(string-append str1 ...) => strN
String-append
returns a new string that contains the
concatenation of the strings passed to it as arguments. The following
rules apply:
(string-append) => "" (string-append "xyz") => "xyz" (string-append "x" "y" "z") => "xyz" (string-append 'non-string) => bottom
The string-append
function conforms to R5RS.
(string-length str) => num
The string-length
function returns the number of
characters contained in the string str. The following rules
apply:
(string-length "") => 0 (string-length "xyz") => 3 (string-length 'non-string) => bottom
The string-length
function conforms to R5RS.
(string-ref str num) => char
String-ref
extracts the character at position
num from the string str. The position of
the first character of a string is zero. Num may not
be negative and it may not be longer than the length of str
minus one. String-ref
returns the extracted character.
The following rules apply:
(string-ref "x" 0) => #\x (string-ref "xyz" 0) => #\x (string-ref "xyz" 2) => #\z (string-ref "xyz" 3) => bottom (string-ref "xyz" -1) => bottom (string-ref 'non-string 0) => bottom (string-ref "xyz" 'non-number) => bottom
The string-ref
function conforms to R5RS.
(substring str1 num1 num2) => str2
The substring
primitive returns a new string containing
a substring of str1. The substring to be extracted is described
by the arguments num1 and num2 as follows:
Num1 is the position of the first character to be extracted,
and num2 is the position of the first character not
to be extracted. Positions start at zero. The following assertion must
hold:
(<= num1 num2 (string-length str1))
The following rules apply:
(substring "" 0 0) => "" (substring "xyz" 0 0) => "" (substring "xyz" 0 3) => "xyz" (substring "xyz" 0 1) => "x" (substring "xyz" 1 3) => "yz" (substring 'non-string 0 0) => bottom (substring "xyz" 'non-number 0) => bottom (substring "xyz" 0 'non-number) => bottom
The substring
function conforms to R5RS.
(delete-file str) => {#t,#f}
(delete-file str)
deletes the file named in str.
It returns #t
, if the given file could be deleted and
otherwise #f
. Possible reasons why a file could not be
deleted include
The following rules apply:
(delete-file "foo") => #t ; file foo deleted (delete-file "foo") => #f ; file foo not deleted (delete-file 'non-string) => bottom
The delete-file
function is specific to Sketchy.
(display expr) => expr
The display
function writes a nicely formatted copy
of expr to the output stream. Formatting includes
the removal of quotation marks of strings and the #\
prefix of chars. The #\space
and #\newline
objects print as a blank character and a newline character.
Display
returns (void)
.
Display
does not print the external
representation of an expression. Use write
to print
unambiguous representations of expressions.
Output is formatted as follows:
(display 'foo) writes foo (display #\a) writes a (display 123) writes 123 (display "hello") writes hello (display "\"hi\"") writes "hi" (display '(x . y)) writes (x . y) (display #\space) writes (display #f) writes #f (display (lambda (x) x)) writes #<closure (x)> (display car) writes #<primitive car> (display cond) writes #<special cond>
The display
function conforms to R5RS.
(eof-object? expr) => {#t,#f}
(Eof-object? x)
evaluates to #t
, if
x is the EOF object and otherwise to #f
.
The EOF object is returned by the read
and
read-char
functions when attempting to read beyond
the end of the input stream.
Because the EOF object has no unambiguous external representation, the following examples use the one defined here:
(define eof (begin (with-output-to-file "empty-file" (lambda () ())) (with-input-from-file "empty-file" read)))
Given above definition, the following rules apply:
(eof-object? eof) => #t (eof-object? 'any-other-object) => #f
The eof-object?
function conforms to R5RS.
(read) => expr
(Read)
reads the external representation of an expression
from the input stream, converts it to internal representation, and
returns it. After reading a single expression, read
returns. Any text following the expression in the input stream will
be left there. Read
is capable of reading any unambiguous
external representation that was written by write
.
When read
is applied to an input stream that does not
hold any input, it returns the EOF object which can be checked using
the eof-object?
function.
The top level loop of the Sketchy interpreter uses read
to parse programs.
Here are sample applications of read
:
(read)foo => foo (read) foo => foo (read) 'foo => 'foo (read) foo bar => foo ; bar is left in the input stream (read) "hello" => "hello" (read) #\x => #\x (read) #\space => #\space (read) #\; => #\; (read) #t => #t (read) (x . y) => (x . y) (read) #<foo> => bottom ; unreadable object
The read
function conforms to R5RS.
(read-char) => char
(Read-char)
reads a single character from the input
stream and returns a matching char object. Unlike read
it does not parse its input, but reads raw characters. If multiple
characters are available from the input stream, read-char
returns only the first one and leaves the remaining input in the
stream. When no characters are available from the input stream,
the EOF object is returned.
Here are sample applications of read-char
:
(read-char)x => #\x (read-char) x => #\space ; x is left in the input stream (read-char)( => #\( (read-char); => ; (read-char) => #\newline
The read-char
function conforms to R5RS.
(with-input-from-file str fun) => expr
The with-input-from-file
function opens the file named
in the string str and connects the input stream to that file.
In this context, it applies the function fun, which must be
a function of zero arguments. As soon as the function returns, the
file is closed and the input stream is connected to the source that
was in effect before.
All applications of read
or read-char
inside of fun will read from the given file.
Applications of with-input-from-file
evaluate to the
value of (fun)
.
In case the specified file does not exist, applications of
with-input-from-file
evaluate to bottom.
A sample application of this function follows. The file foo
is assumed to contain the text (foo "hello" 5)
.
(with-input-from-file "foo" read) => (foo "hello" 5)
The with-input-from-file
function conforms to R5RS.
(with-output-to-file str fun) => expr
The with-output-to-file
function opens the file named
in the string str and connects the output stream to that file.
In this context, it applies the function fun, which must be
a function of zero arguments. As soon as the function returns, the
file is closed and the output stream is connected to the file or device
that was in effect before.
All applications of write
or display
inside of fun will write to the given file. Applications of
with-output-to-file
evaluate to the value of
(fun)
.
In case the file specified in str already exists, its content will be overwritten.
The following sample application of this function writes the
expression (foo "hello" 5)
to the file foo
.
(with-output-to-file "foo" (lambda () (write '(foo "hello" 5)))) => (foo "hello" 5)
The with-output-to-file
function conforms to R5RS.
(write expr) => expr
The write
function writes the external representation
of expr to the output stream. It returns (void)
.
Unambiguous external representation written by write
may be read back using the read
function. The original
expression a and the re-read expression b are
gurranteed to be equal in the sense of (equal? a b)
.
They will not be identical, though (see eq?
).
Output is formatted as follows:
(write 'foo) writes foo (write #\a) writes #\a (write 123) writes 123 (write "hello") writes "hello" (write "\"hi\"") writes "\"hi\"" (write '(x . y)) writes (x . y) (display #\space) writes #\space (write #f) writes #f (write (lambda (x) x)) writes #<closure (x)> (write car) writes #<primitive car> (write cond) writes #<special cond>
The write
function conforms to R5RS.
[<<Reduction Rules] | [Contents] [Index] | [Pre-Defined Symbols>>] |