Functions Top Scopes, Environments, and ObjectsInput and Output Contents

Input and Output

Scam uses a port system for input and output. When Scam starts up, the current input port defaults to stdin (the keyboard) and the current output port defaults to stdout (the screen).

To change these ports, one first creates new port and then sets the port. For example, to read from a file (say "data") instead of the keyboard, first create a file port:

    (define p (open "data" 'read))   ; p points to a port
    (define oldInput (setPort p))
    ...                              ; read stuff from the file data
    (setPort oldInput)               ; restore the old input port

Once the port is set, all input will come from the new port. The setPort function, in addition to setting the port, returns the old port so that it eventually can be restored, if needed.

To change the output port, the procedure is similar, except the symbol 'write is used instead of 'read.

    (define p (open "data" 'write))   ; p points to a port
    (define oldOutput (setPort p))
    ...                               ; write stuff to the file data
    (setPort oldOutput)               ; restore the old output port

Opening a file in 'write mode overwrites the file; to append content to an existing file, use the 'append symbol instead.

Scam only allows a limited number of ports to be open at any given time. If you no longer need a port, close it with the built-in function close, which takes a port as its sole argument:

    (close p)

You can retrieve the current input and output ports with the function calls:




Scam supplies built-in functions for reading characters, integers, reals, strings, and whitespace delimited tokens:

    (assign s (readChar))
    (assign i (readInt))
    (assign r (readReal))
    (assign s (readString))
    (assign t (readToken))
    (assign s (readRawChar))
    (assign u (readUntil stopCharacterString))
    (assign w (readWhile continueCharacterString))

The first five functions listed skip any whitespace preceeding the entity they are to read. The last three functions do not skip whitespace.

Both the readChar and the readToken functions return strings. Scam uses the same rules as the C programming language for what characters constitute an integer and a real. None of these functions take an argument; they use the current input port.

To read a symbol, use the symbol function in conjunction with the readToken function:

    s = symbol(readToken());

To read a line of text, use the built-in readLine function:

    (assign l (readLine))

The readLine function reads up to, and including, the next newline character, but the newline is not part of the returned string.

The pause function always reads from stdin, regardless of the current input port. It reads (and discards) a line of text (up to and including the newline). Its purpose is to pause execution of a program for debugging purposes.

Three other reading functions are useful for scanning text. The first is readRawChar, which returns a string containing the next character in the file, regardless of whether that character is whitespace or not. The second is readUntil, which is passed a string of characters that is used to control the read. For example,

    (readUntil " \t\n")

will start reading at the current point in the file and return a string of all characters read up to point where a character in a given string is encountered. The character that caused the read to stop is pushed back into the input stream and will be the next character read.

The readWhile function is analogous, stopping when a character not in the given string is encountered.


Most output functions write to the current output port.

The simplest output function is display. It takes a single argument, which can be any Scam object:

    (display "Hello, world!\n")

The character sequence \ followed by n indicate that a newline is to be displayed.

More useful than display are the functions print and println, in that they take any number of arguments:

    (print "(f x) is " (f x) "\n")
    (println "(f x) is " (f x))

The println function is just like print, except it outputs a newline after the displaying the last argument. Thus, the two calls above produce the same output.

When a string is printed, the quote marks are not displayed. Likewise, when a symbol is printed, the quote mark is not displayed.

The inspect function prints out the unevaluated version of its argument followed by the arguments evaluation value:

    (inspect (f x))
    -> (f x) is 3

The inspect function always prints to stdout, regardless of the current output port. Like pause, it is useful for debugging.

Pretty printing

The function pp acts much like display, unless it is passed an environment/object. In such cases, it prints out a table listing the variables defined in that scope. Since functions, thunks, exceptions, and errors are all encoded as objects, pp can be used to inspect them in greater detail. For example, consider this definition of square:

    (define (square x)
        (* x x)

Printing the value of square using (print square) yields:

    <function square(x)>

In contrast, using (ppTable square) yields:

    <object 8573>
               label  : closure
             context  : <object 8424>
                name  : square
          parameters  : (x)
                code  : (begin (* x x))

Yet a third way to look at a functgion is with the pretty function:

    (include "pretty.lib")

    (pretty square)


    (define (square x)
        (* x x)

To use the pretty function, you must include the pretty.lib library. You can use the pretty function to display any arbitrary piece of Scam code:

    (pretty '(begin (println "testing square...") (square 3)))


        (println "testing square...")
        (square 3)

To change the level of indent, set the variable __pretty-indent appropriately:

    (assign __pretty-indent "  ")
    (pretty square)


    (define (square x)
      (* x x)

The initial indentation defaults to no indentation. To change this, call the ppCode function:

    (ppCode square "    ")


The fmt function can be used to format numbers and strings if the default formatting is not acceptable. It uses the C programming language formatting scheme, taking a formatting specification as a string, and the item to be formatted. The function returns a string.

For example,

    (string+ "<" (fmt "%6d" 3) ">")
    -> "<     3>"

    (string+ "<" (fmt "%-6d" 3) ">")
    -> "<3     >"

A format specification begins with a percent sign and is usually followed by a number representing the width (in number of characters) of the resulting string. If the width is positive, the item is right justified in the resulting string. If the width is negative, the item is left justified. After any width specification is a character specifying the type of the item to be formatted: d for an integer, f for a real number, and s for a string.

The format specification is quite a bit more sophisticated than shown here. You can read more on a Linux system by typing the command man 3 printf at the system prompt.

Testing for end of file

The eof? function can be used to test whether the last read was successful or not. The function is NOT used to test if the next read would be successful. Here is a typical use of eof? in tokenizing a file:

    (define t (readToken))
    (while (not(eof?))
        (store t)
        (assign t (readToken))

Pushing back a character

Sometimes, it is necessary to read one character too many from the input. This happens in cases like advancing past whitespace in the input. Here is a typical whitespace-clearing loop:

    (define ch (readRawChar))
    (while (space? ch))
        (assign ch (readRawChar))

    ; last character read wasn't whitespace
    ; so push it back to be read again later

    (pushBack ch)

The pushBack function takes a string as its sole argument, but only pushes back the first character of the string; subsequent characters in the string are ignored.

Functions Top Scopes, Environments, and ObjectsInput and Output Contents