forked from google/lisp-koans
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtype-checking.lisp
153 lines (140 loc) · 6.71 KB
/
type-checking.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; There is a type hierarchy in Lisp, based on the set theory.
;;; An object may belong to multiple types at the same time.
;;; Every object is of type T. No object is of type NIL.
(define-test typep
;; TYPEP returns true if the provided object is of the provided type.
(true-or-false? t (typep "hello" 'string))
(true-or-false? t (typep "hello" 'array))
(true-or-false? nil (typep "hello" 'list))
(true-or-false? t (typep "hello" '(simple-array character (5))))
(true-or-false? t (typep '(1 2 3) 'list))
(true-or-false? t (typep 99 'integer))
(true-or-false? t (typep nil 'NULL))
(true-or-false? t (typep 22/7 'ratio))
(true-or-false? t (typep 4.0 'float))
(true-or-false? t (typep #\a 'character))
(true-or-false? t (typep #'length 'function)))
(define-test type-of
;; TYPE-OF returns a type specifier for the object.
(assert-equal 'null (type-of '()))
(assert-equal 'ratio (type-of 4/6)))
(define-test overlapping-types
;; Because Lisp types are mathematical sets, they are allowed to overlap.
(let ((thing '()))
(true-or-false? t (typep thing 'list))
(true-or-false? t (typep thing 'atom))
(true-or-false? t (typep thing 'null))
(true-or-false? t (typep thing 't))))
(define-test fixnum-versus-bignum
;; In Lisp, integers are either fixnums or bignums. Fixnums are handled more
;; efficiently by the implementation, but some large integers can only be
;; represented as bignums.
;; Lisp converts between these two types on the fly. The constants
;; MOST-NEGATIVE-FIXNUM and MOST-POSITIVE-FIXNUM describe the limits for
;; fixnums.
(let ((integer-1 0)
(integer-2 most-positive-fixnum)
(integer-3 (1+ most-positive-fixnum))
(integer-4 (1- most-negative-fixnum)))
(true-or-false? t (typep integer-1 'fixnum))
(true-or-false? nil (typep integer-1 'bignum))
(true-or-false? t (typep integer-2 'fixnum))
(true-or-false? nil (typep integer-2 'bignum))
(true-or-false? nil (typep integer-3 'fixnum))
(true-or-false? t (typep integer-3 'bignum))
(true-or-false? nil (typep integer-4 'fixnum))
(true-or-false? t (typep integer-4 'bignum))
;; Regardless of whether an integer is a fixnum or a bignum, it is still
;; an integer.
(true-or-false? t (typep integer-1 'integer))
(true-or-false? t (typep integer-2 'integer))
(true-or-false? t (typep integer-3 'integer))
(true-or-false? t (typep integer-4 'integer))))
(define-test subtypep
(assert-true (typep 1 'bit))
(assert-true (typep 1 'fixnum))
(assert-true (typep 1 'integer))
(assert-true (typep 2 'integer))
;; The function SUBTYPEP attempts to answer whether one type specifier
;; represents a subtype of the other type specifier.
(true-or-false? t (subtypep 'bit 'integer))
(true-or-false? t (subtypep 'vector 'array))
(true-or-false? t (subtypep 'string 'vector))
(true-or-false? t (subtypep 'null 'list)))
(define-test list-type-specifiers
;; Some type specifiers are lists; this way, they carry more information than
;; type specifiers which are symbols.
(assert-true (typep (make-array 0) '(vector * 0)))
(assert-true (typep (make-array 42) '(vector * 42)))
(assert-true (typep (make-array 42 :element-type 'bit) '(vector bit 42)))
(assert-true (typep (make-array '(4 2)) '(array * (4 2))))
(true-or-false? t (typep (make-array '(3 3)) '(simple-array t (3 3))))
(true-or-false? nil (typep (make-array '(3 2 1)) '(simple-array t (1 2 3)))))
(define-test list-type-specifiers-hierarchy
;; Type specifiers that are lists also follow hierarchy.
(true-or-false? t (subtypep '(simple-array t (3 3)) '(simple-array t *)))
(true-or-false? t (subtypep '(vector double-float 100) '(vector * 100)))
(true-or-false? t (subtypep '(vector double-float 100) '(vector double-float *)))
(true-or-false? t (subtypep '(vector double-float 100) '(vector * *)))
(true-or-false? t (subtypep '(vector double-float 100) '(array * *)))
(true-or-false? t (subtypep '(vector double-float 100) t)))
(define-test type-coercion
(assert-true (typep 0 'integer))
(true-or-false? nil (typep 0 'short-float))
(true-or-false? nil (subtypep 'integer 'short-float))
(true-or-false? nil (subtypep 'short-float 'integer))
;; The function COERCE makes it possible to convert values between some
;; standard types.
(true-or-false? t (typep (coerce 0 'short-float) 'short-float)))
(define-test atoms-are-anything-thats-not-a-cons
;; In Lisp, an atom is anything that is not a cons cell. The function ATOM
;; returns true if its object is an atom.
(true-or-false? t (atom 4))
(true-or-false? nil (atom '(1 2 3 4)))
(true-or-false? nil (atom '(:foo . :bar)))
(true-or-false? t (atom 'symbol))
(true-or-false? t (atom :keyword))
(true-or-false? t (atom #(1 2 3 4 5)))
(true-or-false? t (atom #\A))
(true-or-false? t (atom "string"))
(true-or-false? t (atom (make-array '(4 4)))))
(define-test functionp
;; The function FUNCTIONP returns true if its arguments is a function.
(assert-true (functionp (lambda (a b c) (+ a b c))))
(true-or-false? t (functionp #'make-array))
(true-or-false? nil (functionp 'make-array))
(true-or-false? t (functionp (lambda (x) (* x x))))
(true-or-false? nil (functionp '(lambda (x) (* x x))))
(true-or-false? nil (functionp '(1 2 3)))
(true-or-false? nil (functionp t)))
(define-test other-type-predicates
;; Lisp defines multiple type predicates for standard types..
(true-or-false? t (numberp 999))
(true-or-false? t (listp '(9 9 9)))
(true-or-false? t (integerp 999))
(true-or-false? t (rationalp 9/99))
(true-or-false? t (floatp 9.99))
(true-or-false? t (stringp "nine nine nine"))
(true-or-false? t (characterp #\9))
(true-or-false? t (bit-vector-p #*01001)))
(define-test guess-that-type
;; Fill in the blank with a type specifier that satisfies the following tests.
(let ((type '(simple-array array (5 3 *))))
(assert-true (subtypep type '(simple-array * (* 3 *))))
(assert-true (subtypep type '(simple-array * (5 * *))))
(assert-true (subtypep type '(simple-array array *)))
(assert-true (typep (make-array '(5 3 9) :element-type 'string) type))
(assert-true (typep (make-array '(5 3 33) :element-type 'vector) type))))