Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unreachable Ill-formed if-expression is syntax error in Scheme but not in Common Lisp

I'm trying to get a better understanding of how S-expressions are evaluated in different lisps and wanted to see they would handle interesting ill-formed expressions. I get that Common Lisp and Scheme are totally different languages, but is there a specific difference in their semantics that explains the difference in behavior. For example, Lisp-1s and Lisp-2s have observable differences in their behavior, as do hygienic vs non-hygienic macro systems.

I have a program containing an unreachable ill-formed if expression in Scheme and Common Lisp.

;; foo.scm
(if #t 1 (if))

(display "12")

And the Common Lisp version

;; foo.lisp
(if t 1 (if))

(display "12")

chicken and guile both produce a syntax error.

Chicken:

% chicken foo.scm

Syntax error: (foo.scm:1) in `if' - pair expected

    (if)

    Expansion history:

    <syntax>      (##core#begin (if #t 1 (if)))
    <syntax>      (if #t 1 (if))
    <syntax>      (##core#if #t 1 (if))
    <syntax>      (if)  <--

Guile:

% guile foo.scm
...
.../foo.scm:1:9: source expression failed to match any pattern in form (if)

sbcl and clisp both print 12 and emit no warnings.

SBCL:

% sbcl --load foo.lisp
This is SBCL 1.3.11, an implementation of ANSI Common Lisp.
...
12
0]^D

CLISP

% clisp foo.lisp

"12"
like image 918
Gregory Nisbet Avatar asked Dec 18 '22 12:12

Gregory Nisbet


1 Answers

Implementations of Common Lisp: Interpreter and Compilers

In Common Lisp the type of code execution depends on what the Lisp system implements and how you use it. Often Common Lisp implementations have multiple ways to execute code: an interpreter and one or more compilers. Even a single running implementation may have that and it will allow the user to switch between those.

  • Interpreter: executing directly from the Lisp data structure. It is not an interpreter of virtual machine code, like the JVM. One would not expect that the interpreter validates the full code tree/graph during execution. The interpreter usually looks only at the current top form it executes.

  • Compiler: compiles Lisp code to C, some Byte Code or Machine Code. Since the compiler generates code before the code runs, it will do a syntax check of all the code (for a possible exception, see the remark at the bottom) it sees.

  • Toplevel evaluation: may use an interpreter, a compiler or a mix of both.

GNU CLISP has both an interpreter and a compiler

Example in GNU CLISP:

LOAD of a text file typically uses the interpreter:

[1]> (load "test.lisp")
;; Loading file test.lisp ...
;; Loaded file test.lisp
T

The Interpreter will not detect the error, because it does not check the whole expression for syntactic correctness. Since the else clause with the syntax error is never used, the Interpreter will never look at it.

CLISP also has a compiler:

[2]> (compile-file "test.lisp")
;; Compiling file /tmp/test.lisp ...
** - Continuable Error
in #:|1 1 (IF T 1 ...)-1| in line 1 : Form too short, too few arguments: (IF)
If you continue (by typing 'continue'): Ignore the error and proceed
The following restarts are also available:
ABORT          :R1      Abort main loop

As you see, the CLISP compiler detects the syntax error and gives a clear error message.

SBCL uses a compiler, but also has an interpreter

SBCL by default uses a compiler, which will detect the error. For top-level forms it uses for some forms a simpler evaluation mechanism. One can also switch to an interpreter.

If you write a simple IF form in SBCL, the evaluator does not use full compilation and doesn't catch the error:

CL-USER> (if t 1 (if))
1

If you write the same code inside a function definition, the error gets detected, because function definitions will be compiled by default:

CL-USER> (defun foo () (if t 1 (if)))
; in: DEFUN FOO
;     (IF)
; 
; caught ERROR:
;   error while parsing arguments to special operator IF:
;     too few elements in
;       ()
;     to satisfy lambda list
;       (SB-C::TEST SB-C::THEN &OPTIONAL SB-C::ELSE):
;     between 2 and 3 expected, but got 0
; 
; compilation unit finished
;   caught 1 ERROR condition
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO

If you would switch to the full SBCL interpreter, the error would not be detected at definition time:

CL-USER> (setf *evaluator-mode* :interpret)
:INTERPRET
CL-USER> (defun foo () (if t 1 (if)))
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO

Optimisation and syntax checking

Note that some optimising compilers might not check the syntax of unreachable code.

Summary

In most Common Lisp implementations you need to use the compiler to get full syntax warnings/errors.

like image 132
Rainer Joswig Avatar answered May 15 '23 05:05

Rainer Joswig