built-in types and how to construct aggregate types (e.g., records, classes or structures). The program should demonstrate which of the standard suite of operators (
+ - / * %) manipulate which types and what the resulting type of each operation is. Use it to explain the type system in your language.
Built In Types
All entities are atoms or lists. Lists are aggregate types (see next section). Atoms are either symbols or they are not. Symbols are variables (which may be functions).
All other atoms are numbers, characters, and bits.
Numbers are real and complex. Reals are rationals and floating point values. Rationals are integers and ratios.
Integers are different from many languages in that they do not have an explicit bound. It is possible for optimization purposes to restrict an integer value to a fixnum, but the default will automatically change to a bignum if necessary, which is only bounded by the entire memory capacity. Floating point numbers have four options (short, single, double, or long).
Lists are cons types. A cons has a car, which is the first element of the list, and a cdr, which is the rest of the list. This is simply a linked list format.
Characters start with #\ and follow any character you like, since back-slash make them escaped. So, #\g is the character g.
(typep 45 'real) ;T (typep 45 'integer) ;T (typep 45 'fixnum) ;T (typep #c(45 0) 'real) ;T (typep #c(45 1) 'complex) ;T (typep 1/2 'rational) ;T (typep 1/2 'ratio) ;T (typep 1/2 'float) ;nil (typep .45 'float) ;T (typep .45 'rational) ;T (typep 4.5l4 'long-float) ;T (typep #\g 'standard-char) ;T (typep 0 'bit) ;T (typep (cons 4 5) 'list); T (equal (cons 4 ()) '(4)); T (equal (cons 4 (cons 5 ())) '(4 5)); T
Arrays are lists of specified dimension:
* (setq a1 (make-array '(3 4))) #2A((0 0 0 0) (0 0 0 0) (0 0 0 0)) ;2 dimensional array * (typep #2A((0 0 0 0) (0 0 0 0) (0 0 0 0)) 'array) T * (setq a2 (make-array '(1 2 3))) #3A(((0 0 0) (0 0 0))) ;3 dimensional array * (typep #2A((0 0 0 0) (0 0 0 0) (0 0 0 0)) 'list) NIL
An array can carry any type of objects, and it can also be restricted to carry one particular type.
Vectors are one-dimensional arrays:
* (setq v (make-array '(3))) #(0 0 0) * (typep #(3 2 0) 'vector) T * (typep #2A((0 0 0 0) (0 0 0 0) (0 0 0 0)) 'vector) ;multidimensional NIL
Strings are vectors whose elements are restricted to type
* (setq s1 "wohoo!") "wohoo!" * (typep "wohoo!" 'string) T * (typep "wohoo!" 'vector) T * (typep "1234" 'string) T
Structures create objects which store data. Structures are defined by a special for of Lisp defstruct.
defstruct name slots... - defines a structure type called name (which must be a symbol) with slots specified. Slots can be strings, cons(?), numbers.
* (defstruct icecream flavor price) ; structure type named icecream with slots named flavor and price ICECREAM ; structure evaluates to its name * (setf vanilla (make-icecream :flavor "vanilla" :price 5)) ; give values to the slots of the type icecream #S(ICECREAM :FLAVOR "vanilla" :PRICE 5) * (typep #S(ICECREAM :FLAVOR "vanilla" :PRICE 5) 'icecream) ; is the object of type icecream? T
Common Lisp supports basic arithmetic operations such as addition, subtraction, multiplication, and division for the type number using operators *, /, -, +. If arguments in the operation of the same type, then a value of that type will be returned as a result, except for division performed on integers, which would output a rational number. If the arguments of a different type, then a widening is applied to the narrower type.
* ( + 3/4 3) 15/4 * (+ 3/4 3.0) 3.75 * (/ 3 4) ; division on integers evaluating to a rational number 3/4
Rationals types are a nice feature if you are running mathematical calculations, that require exact precision. The downside is that rational numbers (that are aggregate types of integers) take up more space and slow down the program. If you priority is the speed, not precision, you can specify in the program if you want integer division that evaluates to rationals to be evaluated to floats.
- The question arises then concerning an operation on integer and floating point types. In Common Lisp integer are not restricted in space allocated for them, they can be as big as we want them. This is a special feature of Common Lisp. Usually an operation on integer and a float would output a float. But what would happen if an operation performed onto a big integer and a float?
* (type-of (+ 0.2 (expt 2 ( expt 2 16)))) debugger invoked on a SIMPLE-TYPE-ERROR: Too large to be represented as a SINGLE-FLOAT: ; very big number
A strong typed properties of Common Lisp showed above. The compiler evaluated the number (expt 2 ( expt 2 16))) and then tried to convert it to a single-float, which would not work due to the size of the number, so an error was reported to us. Big integers can have a wider space allocated for them compare to the ones floating points have, so operation on those could not be performed.
Calling an operation on more then two values is the same as calling it onto first two, evaluating the result, calling the operation on that result and the next value and so on:
* (+ 1 2 3) 6 * (+ (+ 1 2) 3) 6 * (* 3 4 5) 60 * (* (* 3 4) 5) 60