Typing variables in Common Lisp
Asked Answered
S

2

6

I read that CL supports optional typing (as in the language has this feature) which I would assume allos for faster code in many cases, but I can't seem to find anything about actually writing typed code. Is it possible to explicitly type code instead of using hacks? (for example, #'vector generates a simple-vector, kinda reminds me of |0 in JS to coerce to an integer)

Or maybe since types are actually CLOS classes (they are, right?) you simply have to #'make-instance an object of type, say, 'integer?

Steamroller answered 31/5, 2017 at 9:5 Comment(3)
Maybe (declare (type integer var)) where var is a variable bound to the value 30 (for example)?Steamroller
Types are separate from CLOS. You can use DECLARE/PROCLAIM/DECLAIM to declare types for variables/functions. For struct or class slots you can use the :TYPE keyword, and for arrays the :ELEMENT-TYPE. For example, (let ((x 10)) (declare (type integer x)) ...). What actually hapens when you declare a type depends on the implementation and optimisation settings. Usually you'll need to favor speed to get optimized code, or safety to get type checking.Relevant
Types are not CLOS classes/objects.. All classes have a corresponding type, but not vice versa. You have no guarantee the code will run any faster with type hinting. IMO that means you can write code and type hint for safety/speed when you need those things, but doing it always is premature optimzationMollee
C
6

Common Lisp allows to define types and to declare types for variables, functions, ...

Types are independent from classes - classes are also types, but types can express more (like the range of integers, the size of an array, the element types of an array, ...).

Declaring types may serve a lot of different purposes:

  • omit runtime type checks in optimized code
  • generation of specialized instructions in optimized code
  • compile-time type checks
  • optimize allocation of data structures
  • runtime type checks
  • documentation

Thus the effects of declaring types with certain compiler settings (debug, space, speed, compilation-speed, safety, ..) is not very simple. A Lisp system may ignore much of the declarations or use them widely. The effects can be very different. In some combination of compiler settings declaring a type may make the code a lot slower, in others it may make it a lot faster.

Also, compilers might be able to do some amount of type inference.

A simple declaration might look like this:

(defun add-integer (a b)
  (declare (integer a b))
  (the integer (+ a b)))

The Lisp system can now do one or more of the following:

  1. ignore the declaration
  2. add runtime checks that a and b are integers and that the result of the + operation is actually an integer (and not a float, rational or complex number)
  3. omit runtime checks for the + operation
  4. generate code for a specialized + operation, which only works on integers
  5. do compile-time type checks

Typical compiler settings to achieve above would be:

  1. safety = 3
  2. safety = 0
  3. safety = 0 and speed = 3

But the exact settings and their meaning might differ and should be documented in the specific Lisp implementation manual.

The types are documented in the ANSI Common Lisp Standard. See Types and Classes.

For the compiler settings see for example: SBCL Compiler or LispWorks, The Compiler.

To study the effect of compilation of type declared code one can use disassemble and time.

Cibis answered 31/5, 2017 at 20:31 Comment(7)
Thanks for the detailed answer! When I need to write fast code (and I think I will need to do so to convince my uncle to use CL on his startup project) I will use your adviceSteamroller
What does the do though? Does it return a value as a certain type?Steamroller
it returns an integer numberCibis
So (the string "Hello") => string, (the simple-vector #(1 2 3)) => simple-vector etc?Steamroller
Check the Hyperpec for THE. lispworks.com/documentation/HyperSpec/Body/s_the.htm#theCibis
Also check FTYPE declarations: lispworks.com/documentation/HyperSpec/Body/d_ftype.htm#ftypeCibis
I see. So to get the most out of typing I would need to compile my code on the highest speed setting and use the and ftype. Thanks!Steamroller
T
3

For performance tuning see https://lispcookbook.github.io/cl-cookbook/performance.html

For compile-time type warnings see https://lispcookbook.github.io/cl-cookbook/type.html

You can give type hints like this:

(defun max-with-type (a b)
    (declare (optimize (speed 3) (safety 0)))
    (declare (type integer a b))
    (max a b))

and use the:

(defun do-some-arithmetic (x)
  (declare (optimize (speed 3) (debug 0) (safety 0)))
  (the fixnum (+ x (square x))))

Using declaim will also give more type warnings at compile time.

It is possible to type variables:

(declaim (type (string) *name*))
(defparameter *name* "book")

We can compose types ((or null string)) and use our owns (declared with deftype).

You can declare function types with declaim, or with declare inside the function:

(declaim (ftype (function (fixnum) fixnum) add))
;;                         ^^input ^^output [optional]
(defun add (n)
    (+ n  1))

With this we get nice type warnings at compile time.

If we change the function to erroneously return a string instead of a fixnum, we get a warning:

(defun add (n)
    (format nil "~a" (+ n  1)))
; caught WARNING:
;   Derived type of ((GET-OUTPUT-STREAM-STRING STREAM)) is
;     (VALUES SIMPLE-STRING &OPTIONAL),
;   conflicting with the declared function return type
;     (VALUES FIXNUM &REST T).
Thursday answered 17/10, 2019 at 17:54 Comment(3)
Does declaim generalize to full static typing, e.g. can you compile time enforce a list of ints vs a list of strings? If I go around declaiming everything will the language feel statically typed or am I going to run into tons of cases where the checking doesn't catch things?Externality
Second option, static typing is limited. But… if you want full ML/Haskell-like static typing à la Hindley-Milner, see the Coalton library, that gives that in its own DSL on top of Common Lisp. It's a great new piece of the ecosystem, developed and used at scale at quantum companies.Thursday
@Elwince I checked it out but the very first issue on their issue tracker is that it fails to prevent your from putting mixed types in a vector which is a pretty basic checking failure :(Externality

© 2022 - 2024 — McMap. All rights reserved.