content/blog/2018/08/types-and-classes-in-common-lisp.markdown @ aa7aebed85bc
Remove the last few remaining Source Hut links
author |
Steve Losh <steve@stevelosh.com> |
date |
Thu, 23 Jan 2020 00:13:09 -0500 |
parents |
fb2a0b170b39 |
children |
(none) |
+++
title = "Types and Classes in Common Lisp"
snip = "They're not the same thing!"
date = 2018-08-30T16:00:00Z
draft = true
+++
One thing that often confuses people new to Common Lisp is the differences
(and interactions) between types and classes in the language. Type and classes
are two completely separate things in Common Lisp, but if you're coming from
modern languages it's easy to get the two blurred and confused. Hopefully this
post will make the distinction more clear.
<div id="toc"></div>
## Objects
Before we dive into defining types and classes we should define what an "object"
is, because the term will come up immediately. The following definition will be
good enough for our purposes here:
**An object is a hunk of bits somewhere in memory.**
Objects are the things the garbage collector manages. They're the things you
pass to functions, and the things you return from them. Objects have identity,
and that identity can be compared with `eq`.
I realize that this is a little handwavey, but I think it's good enough to work
with for now.
TODO values
(There are a few corner cases (e.g. fixnums), but you can safely ignore them
while trying to wrap your head around this post.)
## Types
Types in Common Lisp can be summed up in one single line:
**A type is a set of objects.**
We already saw what objects are. "Set" in this definition is a set in the
mathematical sense: an unordered collection of elements (possibly *infinitely
many* elements) with no duplicates.
That's it. That's all there is to it. This probably seems like a weird
definition if you've never thought much about it before, but let's look at some
examples to see what falls out of it.
### Type Designators
First we need a way to specify types. Common Lisp has a concept called [type
designators][TODO] for this purpose. A type designator is something that
represents the given type.
Let's look at a common type: the set of all integers. Obviously it would be
impractical to talk about this set by listing out all of its members.
A mathematician would denote this type as Z TODO. A Common Lisp programmer
would use the type designator `integer`.
### Being Of a Type
What are some things we might want to do with a type? One thing might be to ask
whether a particular object "is of that type", done with `typep` in Common Lisp
(and `instanceOf` in Java, TODO in Python, etc). But what does this actually
*mean*?
When you're thinking of types as sets of objects, asking whether an object is of
a particular type essentially asking if the object is a member of that set!
Let's look at couple of examples:
(typep 42 'integer)
Here we're asking "Is `42` a member of the set of all integers?"
(typep x 'symbol)
Now we're asking "Is the object that `x` evaluates to a member of the set of all
symbols?".
(typecase foo
(symbol ...a...)
(integer ...b...)
(number ...c...))
And now we're saying "Evaluate `foo`. If the result is a member of the set of
all symbols, do `a`. Otherwise if it's a member of the set of all integers, do
`b`. Otherwise if it's a member of the set of all numbers, do `c`. Otherwise
return `nil`.".
### Subtypes and Supertypes
So checking if an object is of a particular type is simply the set membership
operation. It turns out that other set operations also have useful definitions
when you think this way:
* If `foo` is a supertype of `bar`, that means `foo` is a superset of `bar`.
* If `bar` is a subtype of `foo`, that means `bar` is a subtype of `foo`.
For example:
* The set of all integers is a subset of the set of all real numbers, which
makes `integer` a subtype of `real`.
* The set of all symbols is a superset of the set of all keyword symbols, so
`symbol` is a supertype of `keyword`.
* The set of all floating point numbers is neither a subset nor a superset of
the set of all symbols, so neither is a subtype of the other.
Common Lisp's numeric tower consists of nice sequences of types that get more
and more specific as you take further subsets:
`number` ⊆ `real` ⊆ `rational` ⊆ `integer` ⊆ `fixnum`
### Explicit Designation
There are other ways to designate sets too. You can use the `member` type
designator to just list out the members of a set by hand if you want:
(member 1 2 3) ; => designates the set {1, 2, 3}
### Everything and Nothing
The type `t` is the set of all objects. The type `nil` is the empty set.
This last one can sometimes cause confusion. The symbol `nil` is *not* of type
`nil` because the symbol `nil` is not a member of the empty set (because it's
empty!). If you want to talk about the set containing the symbol `nil`, that's
called `null`:
```lisp
(typep nil nil) ; => NIL, nil is NOT a member of the empty set
(typep nil null) ; => T, nil IS a member of { nil }
```
### Type Combinations
Let's look at some more set operations. Set complement is probably the
simplest, and Common Lisp supports this with the `not` compound type specifier
TODO:
```lisp
(typep 1/2 '(not integer)) ; => T
(typep "hello" '(not integer)) ; => T
(typep 42 '(not integer)) ; => NIL
```
Set union is covered with `and`:
```lisp
(typep 1 '(or integer string)) ; => T
(typep "hi" '(or integer string)) ; => T
(typep :what '(or integer string)) ; => NIL
```
And of course set intersection is done with `and`:
```lisp
(typep 1 )
```
## Classes
## Blurring the Line