Predicates & Type Checking
Predicates return #t or #f and conventionally end with ?.
Emptiness Predicates
These three predicates overlap but are not interchangeable. null? returns #t for both '() and nil — it tests for "absence of a value or empty list". nil? is true only for the nil value itself (not for '()). empty? is the broadest: it accepts nil, strings, lists, vectors, maps, and other collections, returning #t when the value has no elements. Reach for empty? when you have a collection of any shape; reach for nil? when you specifically need to distinguish nil from '().
null?
Test if a value is the empty list or nil.
(null? '()) ;; => #t
(null? nil) ;; => #t
(null? '(1)) ;; => #fnil?
Test if a value is nil specifically (not the empty list).
(nil? nil) ;; => #t
(nil? '()) ;; => #f
(nil? 0) ;; => #fempty?
Test if a collection, string, or nil is empty. Accepts strings, lists, vectors, maps, and nil.
(empty? "") ;; => #t
(empty? '()) ;; => #t
(empty? nil) ;; => #t
(empty? "hello") ;; => #f
(empty? [1 2 3]) ;; => #fCollection Predicates
list?
Test if a value is a list.
(list? '(1)) ; => #t
(list? 42) ; => #fpair?
Test if a value is a non-empty list (Scheme compatibility).
(pair? '(1 2)) ; => #t
(pair? '()) ; => #fvector?
Test if a value is a vector.
(vector? [1]) ; => #t
(vector? '(1)) ; => #fmap?
Test if a value is a map.
(map? {:a 1}) ; => #t
(map? '()) ; => #fNumeric Predicates
Sema implements the full R7RS numeric tower, so two families of predicates apply to numbers: type/level tests (number?, integer?, rational?, real?, complex?, float?) and exactness tests (exact?, inexact?, exact-integer?). The type predicates nest — every integer is rational, every rational is real, every real is complex — so complex? is the widest and true for all numbers.
number?
Test if a value is a number — anything in the tower (integer, bignum, rational, float, or complex). Equivalent to complex?.
(number? 42) ; => #t
(number? 3.14) ; => #t
(number? 1/3) ; => #t
(number? "42") ; => #finteger?
Test if a value is an integer, per R7RS: true for any exact integer (including bignums) and for an integer-valued float like 3.0. A float with a fractional part is not an integer. To exclude integer-valued floats, use exact-integer?; to test representation, use float?.
(integer? 42) ; => #t
(integer? 3.14) ; => #f
(integer? 3.0) ; => #t ; integer-valued floatrational?
Test if a number is rational — exact and expressible as a ratio of two integers. Every exact integer and exact rational qualifies; floats and non-real complex numbers do not. (This tracks exactness, so it is stricter than strict R7RS where a finite float is also rational.)
(rational? 42) ; => #t
(rational? 1/3) ; => #t
(rational? 3.14) ; => #f
(rational? 3+4i) ; => #freal?
Test if a number is real — has no non-zero imaginary part. Every integer, rational, and float is real; real? is false only for a complex with a genuine imaginary component. A complex whose imaginary part is exact zero collapses to a real, so 3+0i is real.
(real? 42) ; => #t
(real? 3.14) ; => #t
(real? 3+4i) ; => #f
(real? 3+0i) ; => #tcomplex?
Test if a value is a number. In R7RS the number types nest, so complex? is true for every number in the tower and false only for non-numbers. It is the widest numeric predicate.
(complex? 42) ; => #t
(complex? 3.14) ; => #t
(complex? 3+4i) ; => #t
(complex? "hi") ; => #ffloat?
Test if a value is a floating-point number.
(float? 3.14) ; => #t
(float? 42) ; => #fexact?
Test if a number is exact — represented without floating point. Exact numbers are integers, exact rationals, and complex numbers whose parts are both exact. The complement of inexact? on numbers.
(exact? 42) ; => #t
(exact? 1/3) ; => #t
(exact? 3.14) ; => #f
(exact? 3+4i) ; => #t
(exact? 3.0+4i) ; => #finexact?
Test if a number is inexact — carries a floating-point component. True for any float and for any complex with at least one inexact part. The complement of exact? on numbers.
(inexact? 42) ; => #f
(inexact? 3.14) ; => #t
(inexact? 1/3) ; => #f
(inexact? 3.0+4i) ; => #texact-integer?
Test if a value is an exact integer — true exactly when both exact? and integer? hold. Stricter than a bare integer?: 2.0 is an integer value but inexact, so it fails.
(exact-integer? 42) ; => #t
(exact-integer? 1/2) ; => #f
(exact-integer? 2.0) ; => #f
(exact-integer? 3+0i) ; => #tzero?
Test if a number is zero.
(zero? 0) ; => #t
(zero? 1) ; => #feven?
Test if an integer is even.
(even? 4) ; => #t
(even? 3) ; => #fodd?
Test if an integer is odd.
(odd? 3) ; => #t
(odd? 4) ; => #fpositive?
Test if a number is positive.
(positive? 1) ; => #t
(positive? -1) ; => #fnegative?
Test if a number is negative.
(negative? -1) ; => #t
(negative? 1) ; => #fType Predicates
string?
Test if a value is a string.
(string? "hi") ; => #t
(string? 42) ; => #fsymbol?
Test if a value is a symbol.
(symbol? 'x) ; => #t
(symbol? "x") ; => #fkeyword?
Test if a value is a keyword.
(keyword? :k) ; => #t
(keyword? "k") ; => #fchar?
Test if a value is a character.
(char? #\a) ; => #t
(char? "a") ; => #fbool?
Test if a value is a boolean. boolean? is an alias.
(bool? #t) ; => #t
(bool? 0) ; => #ffn?
Test if a value is a function. procedure? is an alias.
(fn? car) ; => #t
(fn? 42) ; => #frecord?
Test if a value is a record instance.
(record? my-record) ; => #t
(record? 42) ; => #fbytevector?
Test if a value is a bytevector.
(bytevector? #u8()) ; => #t
(bytevector? '()) ; => #fPromise Predicates
promise?
Test if a value is a promise (created with delay).
(promise? (delay 1)) ; => #t
(promise? 42) ; => #fpromise-forced?
Test if a promise has been forced (evaluated).
(define p (delay (+ 1 2)))
(promise-forced? p) ; => #f
(force p)
(promise-forced? p) ; => #tEquality
eq?
Test structural equality. equal? is an alias.
(eq? 'a 'a) ; => #t
(eq? '(1 2) '(1 2)) ; => #t
(eq? 1 2) ; => #f=
Equality. For numbers this is numeric equality (so (= 1 1.0) is #t); for non-numbers it falls back to structural equality. Unlike < / >, comparing non-numbers does not error.
(= 1 1) ; => #t
(= 1 1.0) ; => #t
(= 1 2) ; => #f
(= "abc" "abc") ; => #t (structural, not an error)LLM Type Predicates
prompt?
Test if a value is an LLM prompt.
(prompt? (prompt (user "hi"))) ; => #tmessage?
Test if a value is an LLM message.
(message? (message :user "hi")) ; => #tconversation?
Test if a value is a conversation.
(conversation? (conversation/new {})) ; => #ttool?
Test if a value is a tool definition.
(deftool my-tool "A test tool" {:x {:type :string}} (lambda (x) x))
(tool? my-tool) ; => #t
(tool? 42) ; => #fagent?
Test if a value is an agent.
(defagent my-agent {:system "test"})
(agent? my-agent) ; => #t
(agent? 42) ; => #f