Type-rich programming in Common Lisp?
Asked Answered
C

1

9

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?

Carin answered 4/4, 2016 at 1:27 Comment(1)
See Conversion of Units of Measurement by Gordon S. Novak Jr.Borehole
N
7

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.

Northey answered 4/4, 2016 at 4:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.