Reputation: 1758
I read an article about static typing the other day (https://bsamuels.net/2013/11/20/static-typing.html) that described an interesting concept called "type-rich programming", where you as a programmer define types that, to the machine, are simply aliases for existing types (such as integers or floats), but to you they describe the difference between different quantities that could be represented using those machine types (e.g., seconds and meters could both be represented by doubles, but you certainly wouldn't want to add them together).
I know that Common Lisp is a dynamically-typed language. However, I also know that some compilers (such as the one that I use, SBCL) will do some limited type checking if I use the
and check-type
. How can I create type aliases so that I can provide richer types to SBCL? Or, if not that, then how can I get something that looks sort of like type-rich programming in Common Lisp?
Upvotes: 9
Views: 746
Reputation: 8411
Common Lisp has DEFTYPE
for defining new types. For example:
(defun secondsp (s)
(<= 0 s 59))
(deftype seconds ()
'(and number (satisfies secondsp)))
(let ((s 0))
(declare (type seconds s))
(loop
repeat 60 ;should cause an error when S becomes 60
do (incf s)
do (write-char #\.)))
It doesn't prevent you from adding seconds and meters together though:
(deftype meters ()
'number)
(let ((s 30)
(m 15))
(declare (type seconds s)
(type meters m))
(+ s m))
;=> 45
You could create a function that uses CHECK-TYPE
or declarations to check that the value is a valid one for seconds:
;; with CHECK-TYPE and THE
(defun add-seconds (s1 s2)
(check-type s1 seconds)
(check-type s2 seconds)
(the seconds (+ s1 s2)))
;; With declarations
(declaim (ftype (function (seconds seconds) seconds) add-seconds-decl))
(defun add-seconds-decl (s1 s2)
(+ s1 s2))
But that will only check that the value is valid second. It doesn't care if you declared the variable to be meters since the function is only passed the value.
(let ((s1 30)
(s2 15)
(m 25))
(declare (type seconds s1 s2)
(type meters m))
(format t "~&S1 + S2 = ~a" (add-seconds-decl s1 s2))
(format t "~&S1 + M = ~a" (add-seconds-decl s1 m)))
;; S1 + S2 = 45
;; S1 + M = 55
If you want to enforce that seconds and meters are never added together, you should just use classes and objects.
Upvotes: 7