ShenDoc 30


 

 

The Semantics of Symbols in Shen and KLambda

Qi II and Common Lisp are all members of the Lisp family of languages. In Lisp, symbols are often semantically context sensitive; that is, the interpretation of a symbol may depend on the context in which it occurs. For example in this Common Lisp expression; '(list 'John 'put 'the 'car 'into 'reverse)', or in Qi II '[John put the car into reverse]', the symbol 'reverse' denotes a symbol. But in Common Lisp the expressions '(reverse '(1 2 3))' (in Qi II '(reverse [1 2 3])') and '(mapcar 'reverse '((1 2) (2 3) (3 4)))', (in Qi II '(map reverse [[1 2] [2 3] [3 4]])'), the symbol 'reverse' actually denotes a function.

That means in the Lisp family, symbols are semantically context sensitive. At the head of an application they can only denote a function and there is no ambiguity; but within the body of the application they can denote either a function or the symbol quoted. Lets us say a symbol is idle in Qi if it is used without naming anything.

To help reading, Common Lisp includes the expression 'function' which disambiguates idle symbols from the other kind; the expression '(mapcar (function reverse) '((1 2) (2 3) (3 4)))' does what '(mapcar 'reverse '((1 2) (2 3) (3 4)))' does but the semantics of 'reverse' is clarified to show that the programmer is referring to the function not the symbol itself.

Qi I and Qi II followed Common Lisp in making symbols semantically context sensitive, relying on the Common Lisp compiler to decipher the ambiguity and make the correct choice. Being specifically tailored for multiplatform development, Shen can make no such assumption. It is quite possible that the native platform does not support symbols as data objects in the manner of Lisp; in which case it is possible to construct a simulation of Lisp symbols by using tagged 2 element vectors (one element a tag to show it is impersonating a symbol, the other to hold a string representation of the symbol; see Coping with Missing Symbols in the document of porting).

However the problem arises of the context sensitivity of symbols; when should a symbol should be 'vectorised' in a non-Lisp language and when not? For instance, if the symbol reverse is vectorised in the context '(map reverse [[1 2] [2 3] [3 4]])', then the map function will crash, since it receives a vector and not a function as an argument. But in the context [John put the car into reverse], it is right to vectorise the symbol reverse because it is not calling a function. How does one proceed?

There are several solutions to this problem. One is to simply avoid idle symbols and insist that strings do all the work of idle symbols. This follows the path of languages like ML where idle symbols do not exist. It would mean that in Shen, [p & q] could not be written; only ["p" "&" "q"]. However this would probably not appeal to those Lispers, like myself, who enjoy the convenience of working with idle symbols.

The second is to treat all non-variable symbols in the body of an application (apart from the leading symbol which must denote a function or procedure of some form) as idle, thus vectorising them. In this case, to prevent any higher-order function H from failing, H must, when applying any vectorised symbol S, look for the function F associated with S and use F instead. This introduces a delay into the execution of H and moreover means that native higher-order functions in the platform, which are not set up to interface to vectorised symbols, will still fail in the manner described above.

The third solution is to provide 'function' as a means of disambiguation and this is what Shen does. Hence any symbol S which is an argument to an application is treated as idle. But if S is enclosed as in '(function S)', the whole expression denotes a function. Hence to write portable Shen; one should avoid writing an expression like '(map reverse [[1 2] [2 3] [3 4]])' and write '(map (function reverse) [[1 2] [2 3] [3 4]])'. The former construction will certainly work under a Common Lisp platform, but the latter will run under Common Lisp and platforms outside the Lisp family.

Note in version 18 and after, 'function' is statically compiled away where possible in favour of a lambda form ('(function union)' is '(/. X Y (union X Y))'). When this is not possible, because function is used dynamically, a lookup table is used to find the appropriate lambda form. zero-place functions are not admissable arguments to 'function'.

Note in S version kernels, 'function' is optionally dispensable in favour of 'fn'. S versions maintain a rigorous distinction between a symbol and the function it designates so that 'reverse : (list A) --> (list A)' is not asserted. Instead '(fn reverse) : (list A) --> (list A)' is used. The old regime was a hangover from the early implementations of Shen in Lisp.

Acknowledgements

History
Basic Types in Shen and Kλ
The Primitive Functions of Kλ
The Syntax of Kλ
Notes on the Implementation of Kλ
Boolean Operators
The Syntax of Symbols
The Semantics of Symbols in Shen and Kλ
Packages
Prolog
Shen-YACC
Strings
Strings and Pattern Matching
Lists
Streams
Character Streams and Byte Streams
Bytes and Unicode
Reader Macros
Vectors
Standard Vectors and Pattern Matching
Non-standard Vectors and Tuples
Equality
I/O
Generic Functions
Eval
Type Declarations
External Global Variables
Property Lists and Hashing
Error Handling
Numbers
Floats and Integers
The Timer
Comments
Special Forms

Built by Shen Technology (c) Mark Tarver, September 2021