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. |