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

> Type declarations in Common Lisp support (safety <n>) and (speed <n>) syntax

To be pedantic, those aren't type declarations. More importantly, the (un)safety of a type declaration isn't predicated on any specific safety/speed settings. The mere use of a type declaration implies that the programmer certifies their accuracy inside its scope; an additional optimize declaration is not required.

It bears repeating: While Common Lisp has "type declarations", they don't behave as they do in statically typed languages. The are certificates, assurances by the programmer to be exploited at will by the implementation.



> To be pedantic, those aren't type declarations.

They are values of optimization qualities and guide the compiler strategy.

> More importantly, the (un)safety of type declaration isn't predicated on any specific safety/speed settings. The mere use of a type declaration implies that the programmer certifies their accuracy inside its scope;

With an optimize quality 3 for SAFETY a compiler will not reduce safety, even though there are type declarations.

The Common Lisp standard REQUIRES that safety at 3 causes 'safe code':

http://www.lispworks.com/documentation/HyperSpec/Body/03_eaa...

Which usually means full runtime checks in interpreted and compiled code, calls, system calls, ...

> an additional optimize declaration is not required.

Sure you need to have the right compiler qualities set to see those effects of your type declarations.

LispWorks:

    CL-USER 4 > (proclaim '(optimize (safety 3)))
    NIL

    CL-USER 5 > (defun foo (x)
                  (declare (string x))
                  (+ x 2))
    FOO

    CL-USER 6 > (compile 'foo)
    FOO
    NIL
    NIL

    CL-USER 7 > (foo 'bar)

    Error: In + of (BAR 2) arguments should be of type NUMBER.
      1 (continue) Return a value to use.
      2 Supply a new first argument.
      3 (abort) Return to top loop level 0.

    Type :b for backtrace or :c <option number> to proceed.
    Type :bug-form "<subject>" for a bug report template or :? for other options.

    CL-USER 8 : 1 > 
Oh, the LispWorks compiler has ignored the type declaration at safety 3. The runtime checks are still there. The code has the same safety as without a type declaration.


Violation of a type declaration is UB in safe code. SBCL obviously makes use of that fact when inserting assertions.


Undefined behavior gives the implementation the freedom to insert any behavior whatsoever (like the proverbial demons flying out of the nose). An assertion isn't any behavior, it is a form of run-time error checking.

Run-time error checking is what the "safety" optimization option means!

From CLHS:

  Name               Meaning                            
  compilation-speed  speed of the compilation process   
  debug              ease of debugging                  
  safety             run-time error checking            
  space              both code size and run-time space  
  speed              speed of the object code  
(optimize (safety 3)) means "greatest possible run-time error checking". Why wouldn't the implementation insert error checking based on the user's type declaration, when asked to provide the greatest possible run-time error checking?


> An assertion isn't any behavior

It is. For example, a failed assertion inhibits effects that should have occurred before the "actual" type error happens.

The only reason SBCL is able to insert assertions into safe code without giving up conforming implementation status is that, as you say, UB allows anything, and violating a declaration is UB even in safe code.


There isn't necessarily an actual type error! Remember, the declaration provided by the user can be an inaccurate estimate of what the real type constraint is at that program node. The program might in fact require a string there, but the programmer's declaration says integer. So the assertion goes off when a string value occurs, when that would in fact correct in the absence of the declaration.

This alteration of behavior by the assertion is not an arbitrary choice of behavior; it is a diagnostic behavior, in line with the diagnostic meaning of safety.

ANSI CL does literally say under "Declarations" that "The consequences are undefined if a program violates a declaration or a proclamation". Yet, it's not reasonable to allow any behavior whatsoever if a type declaration is violated by a run-time situation, in safe code. It just makes no sense at all to allow a crash, or random bits being silently flipped, etc. Given that safe code, in the absence of declarations, catches type errors; why would declarations regress from that, in safe code.


If you re-read everything in this subthread, you'll find a lot of argument from seasoned lispers that I can and should ignore the fact that type declarations are unsafe even at the highest safety settings because of "what implementations do."

If this discussion had taken place before SBCL, the logical conclusion from what these seasoned lispers say would have been that I can and should use type declarations in safe code for documentation purposes (say), because implementations ignore them at high safety, e.g. from lispm: "Oh, the LispWorks compiler has ignored the type declaration at safety 3. ... The code has the same safety as without a type declaration."

If I had followed such advice from seasoned lispers, liberally putting type declarations into safe code for documentation (say), SBCL's new behavior would have burned me, causing aborts in perfectly working code just because I got my "documentation" wrong. Some observations:

(1) SBCL would be allowed to burn me because I invoked UB. Nothing else gives them license to break working code in the presence of a wrong declaration, but they have that license, and I can't sue.

(2) I shouldn't have listened to lispm. Risking UB in safe code because most existing implementations wouldn't have burned me (before SBCL) would have been bad advice. Peppering safe code with declarations does nothing to improve safety, risking UB can only ever deduct from safety.

(3) As a consequence of (2), encouraging the use of declarations everywhere is wrong. To do so, in the words of PuercoPop, is "missing the point of the Lisp's type system." SBCL is wrong to do it.


> If you re-read everything in this subthread, you'll find a lot of argument from seasoned lispers that I can and should ignore the fact that type declarations are unsafe even at the highest safety settings because of "what implementations do."

The word "safe" in Lisp is tied to run-time checks.

"Less safe" means fewer run-time checks.

If a declaration causes a check to be inserted which signals a condition in code that otherwise would not, that isn't "unsafe"; it is something else. "Unsafe" in Common Lisp specifically means that a check didn't take place which normally would take place.

A monotonic increase in the variety of checks performed at run-time cannot be called "unsafe"; that amounts to an incorrect use of the ANSI document's defined term.

You can say that adding declarations to a program is "risky": it creates the risk that conditions will be signaled.

Those extra behaviors are in fact allowed because the program has violated a declaration, which is undefined behavior. That is a very general statement in the standard which is not expected to have malicious interpretations. Declarations are part of the program and can change its behavior; it is not spelled out in detail how, but the optimization parameters like safety and speed have obvious interpretations.

If a declaration is processed for safe code, and a behavior is added other than a diagnostic behavior, that could be characterized as malicious. For instance, supposed the program abruptly stops, without any diagnostic, so that it is not known why. That would not be acceptable. It would not be acceptable for a variable to be mysteriously altered, and for computation to continue, so that a problem occurs later which is nigh impossible to trace back to the violated declaration.

If you think the implementors are malicious, use something else. Malice could be perpetrated in almost any area of an implementation.

Think about it. Suppose you're hoping to use declarations to speed up code. You add the declarations and keep the code safe at first. If declarations are completely ignored, you're not getting any help! The code is working exactly as before and then when you drop safety and add speed, it fails. It fails not in nice ways, but catastrophically, due to reasons that could have been caught had the declarations been processed in safe mode.


> type declarations are unsafe even at the highest safety settings because of "what implementations do."

Type declarations are not unsafe at highest safety settings in most Common Lisp implementations.

Personally I write code for real actual implementations and their documented behavior - and not just for a spec. They implement the pragmatic part of a programming language, extend it with various features. The CL spec for example says nothing about GC - which would make memory allocation 'unsafe' when we follow your arguments - fortunately implementations provide GCs. Similar, implementations provide a mode when safety = 3 where full runtime type checks are enabled.

I for example sometimes exploit that parts of the implementation is using CLOS for built-in functionality where the spec does not require the use of CLOS (streams is an example) - knowing that this code will not run in some - often also uninteresting for me - implementations.

> SBCL's new behavior would have burned me, causing aborts in perfectly working code just because I got my "documentation" wrong.

That's true, SBCL tells you that your documentation is wrong. Which is a good thing. Somebody would read your code and would be confused by your wrong type declarations.

Actually: wrong type declarations for optimization purposes is the real danger. SBCL helps to find the problems.

> encouraging the use of declarations everywhere is wrong. To do so, in the words of PuercoPop, is "missing the point of the Lisp's type system." SBCL is wrong to do it.

That's not what SBCL does. SBCL allows you to add declarations. But also SBCL does type inference, so types get propagated without the need to type everything. Most better Lisp compilers do forms of type inference. I also gave you an example where SBCL takes advantage of method argument lists - thus you don't need to add type declarations, since the method already tells which class the argument is of.

Advice: follow the spec, but understand the broader Common Lisp tradition codified in its implementation.


Exactly. Most implementation will ignore type declarations in safe code. Adding or removing those has no effect.

SBCL honors type declarations. Thus it is defined behavior. SBCL defines it.

I propose to give up the irrational fear of added services provided by SBCL and actually use it as an additional tool.




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

Search: