Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Type hints are not static proofs of type-correctness, though, unless SBCL's warnings have gotten ridiculously more intelligent.


Your notion of 'type-correctness' seems fairly static and you don't know Common Lisp either. A type hint in Common Lisp is the same as a type declaration. Common Lisp just let's you declare the types optionally.

If you declare a type for a value and then try to store a value of an incorrect type then SBCL(and many other CL compilers) will respond with a compiliation error(not a warning) :

  ; in: LAMBDA NIL                                                                                               
  ;     (TYPE (SINGLE-FLOAT NODE))                                                                               
  ;                                                                                                              
  ; caught ERROR:                                                                                                
  ;   Bound is not *, a SINGLE-FLOAT or a list of a SINGLE-FLOAT: NODE                                           
  ;                                                                                                              
  ; compilation unit finished                                                                                    
  ;   caught 1 ERROR condition                                                                                   
                                                                                                                 
  debugger invoked on a SB-INT:COMPILED-PROGRAM-ERROR in thread #<THREAD "initial thread" RUNNING {1002A5A051}>: 
    Execution of a form compiled with errors.                                                                    
  Form:                                                                                                          
    #'(NAMED-LAMBDA TRAVERSE-PRE-ORDER (NODE FUN)                                                                
                  (DECLARE (TYPE (SINGLE-FLOAT NODE))                                                            
                   (IF NODE                                                                                      
                       (PROGN                                                                                    
                        (APPLY FUN (LIST NODE))                                                                  
                        (IF (BTNODE-LEFT NODE)                                                                   
                            (TRAVERSE-PRE-ORDER (BTNODE-LEFT NODE) FUN))                                         
                        (IF (BTNODE-RIGHT NODE)                                                                  
                            (TRAVERSE-PRE-ORDER (BTNODE-RIGHT NODE) FUN)))))                                     
                  (BLOCK TRAVERSE-PRE-ORDER))                                                                    
  Compile-time error:                                                                                            
    Bound is not *, a SINGLE-FLOAT or a list of a SINGLE-FLOAT: NODE                                             
                                                                                                                 
  Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.                                               
                                                                                                                 
  restarts (invokable by number or by possibly-abbreviated name):                                                
    0: [CONTINUE] Ignore runtime option --load "tree-traversal.lisp".                                            
    1: [ABORT   ] Skip rest of --eval and --load options.                                                        
    2:            Skip to toplevel READ/EVAL/PRINT loop.                                                         
    3: [QUIT    ] Quit SBCL (calling #'QUIT, killing the process).                                               
                                                                                                                 
  ((LAMBDA ()))


Can you get a compilation error for

  (defun foo (x)
    (car x))
because x is not known to be a list? I've never seen a lisp do that, though at times I wanted it.


  (defun foo (x)
    (declare (type list x))
    (car x))


Sorry, still just a warning:

$ sbcl This is SBCL 1.0.18.debian, an implementation of ANSI Common Lisp. More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty. It is mostly in the public domain; some portions are provided under BSD-style licenses. See the CREDITS and COPYING files in the distribution for more information. * (defun foo (x) (declare (type array x)) (car x)) ; in: LAMBDA NIL ; (CAR X) ; ; note: deleting unreachable code ; ; caught WARNING: ; Asserted type LIST conflicts with derived type (VALUES ARRAY &OPTIONAL). ; See also: ; The SBCL Manual, Node "Handling of Types" ; ; compilation unit finished ; caught 1 WARNING condition ; printed 1 note

FOO


First of all, you have modified the function that I posted (you changed the type declaration from a list to an array). Second, you do not read your compiler warning messages. SBCL is trying to tell you that you shouldn't type something as an array when you are clearly attempting to use it as a list e.g. calling the CAR function on an array makes no sense. A list and an array in Lisp are not the same. The CAR function in Lisp only works on lists.

  * (defun foo (x) (declare (type array x)) (car x))
  ; in: LAMBDA NIL
  ;     (CAR X)  
  ;
  ; caught WARNING: 
  ;   Asserted type LIST conflicts with derived type (VALUES  ARRAY &OPTIONAL).
  ;   See also:
  ;     The SBCL Manual, Node "Handling of Types"
It's correctly warning you before you even try and call the foo function, because it will fail no matter what you pass to it !


> First of all, you have modified the function that I posted (you changed the type declaration from a list to an array).

How would I demonstrate SBCL's ability to detect type errors except by making a type error? Your example showed nothing; 'type' could be a null-op for all it does.

And checks about primitive types are all well and good, but how far does it go? Not being much of a lisper, it's hard for me to give whole classes of example where SBCL's warnings are non-existent, incorrect, or buried under a flurry of others. If I declare a custom CLOS object with multiple fields/methods/whatever and call a field/method which doesn't exist, is SBCL still smart enough to warn me? I dunno.


There is no concept of a 'primitive' type in Common Lisp. It handles type-checking for built-in types or for your own custom declared types. CL's type system is very powerful.

Here's a type-checking example related to CLOS :

  ;; High safety settings are required by SBCL for type checking of slots  
  ;; See --> http://www.apl.jhu.edu/~hall/Lisp-Notes/Defclass.html         
                                                                           
  (declaim (optimize                                                       
            (speed 0) (compilation-speed 0) (safety 3) (debug 3)))         
                                                                           
  (defclass circle()                                                       
    ((center :type cons                                                    
             :accessor circle-center                                       
             :initarg :center)                                             
     (radius :type fixnum                                                  
             :accessor circle-radius                                       
             :initarg :radius)))                                           
                                                                           
  (defmethod circle-area ((x circle))                                      
    (* pi (expt (circle-radius x) 2)))                                     
                                                                           
                                                                           
  (let ((c (make-instance 'circle :center '(0 0) :radius 4)))              
                                                                           
    (format t "~A~%" (circle-area c))                                      
                                                                           
    (setf (circle-center c) '(2 3))                                        
    (setf (circle-radius c) 7)                                             
                                                                           
    ;; this will fail at compile with a TYPE-ERROR                         
    ;(setf (circle-radius c) "B")                                          
                                                                           
    ;; this will fail at compile with a UNDEFINED-FUNCTION                 
    ;; because the method does not exist                                   
    ;(location c)                                                          
    )


> (let ((c (make-instance 'circle :center '(0 0) :radius 4))) ...

Erm, what? This is supposed to show smart compile-time checking about runtime behavior?? This no more demonstrates what you apparently think it demonstrates than I could demonstrate Haskell's type-soundness by taking some Template Haskell, inserting 'head []', and saying 'it crashes during compilation, eureka!'

What would be fairer would be wrapping all that in a '(defun foo () )', in which case one doesn't get any warning relating to the 'location c' or "B":

* (load "foo.cl") STYLE-WARNING: Implicitly creating new generic function CIRCLE-AREA. ; in: LAMBDA NIL ; ((LET ((C (MAKE-INSTANCE 'CIRCLE :CENTER '# :RADIUS 4))) ; (FORMAT T "~A~%" (CIRCLE-AREA C)) ; (SETF (CIRCLE-CENTER C) '(2 3)) ; (SETF (CIRCLE-RADIUS C) 7) ; (SETF (CIRCLE-RADIUS C) "B") ; (LOCATION C))) ; ; caught ERROR: ; illegal function call ; ; compilation unit finished ; caught 1 ERROR condition

T


It's runtime checking. I really don't care though. Common Lisp just has a different approach, it sits between static typing and dynamic typing. It's type declarations are for optimization. Which I see as the main benefit anyway.

Besides, I've been around long enough to have seen applications that were implemented using statically-typed languages, simply explode. Static typing, IMHO, is extremely overrated. Since many statically typed languages make it easy to cast one type into another, you can't trust it to be anything more than an optimization hint.

This been quite the diversion(thanks). I'm mainly interested in meta-programming in Haskell & how it compares with Lisp.


Actually, if I understood the HyperSpec correctly, in theory a conformant Common Lisp implementation would be allowed to just crash if you declared that x is a list and then you call it with something else. By declaring the type like this, you're telling the compiler: "I assure you that x will definitely be a list".

Of course, in practice implementations like to check everything by default.


The compilation parameters speed and safety will determine what happens when something goes wrong.

For example, the following global settings would be considered fast but dangerous :

  (declaim (optimize 
     (speed 3) (compilation-speed 0) (safety 0) (debug 0)))
These would be safe but slower :

  (declaim (optimize 
     (speed 0) (compilation-speed 0) (safety 3) (debug 3)))




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: