Wednesday, February 12, 2020

A polygot program puzzle

Can you come up with an expression that evaluates to the symbol 'scheme in a Scheme system, but evaluates to the symbol 'common-lisp in a Common Lisp system?

11 comments:

Lars Brinkhoff said...

I was thinking something like this but apparently my Scheme is too old.

*:scheme
This is SCHEME (DSK:QUUX;SCHINT LSP) running in LISP 2154.
It is 4 days, 18 hours, 3 minutes, and 12 seconds past the full moon.
The sun is 27*11'12" south of east, 8*39'25" above the horizon.
That means it is now 7:39 AM on Thursday, February 13, 2020.
SCHEME: Top Level
==> (if nil 'scheme 'common-lisp)
COMMON-LISP

Peter Stirling said...

A form that exemplifies the prissy sensibilities of scheme

Anonymous said...

Just quote nil

(if 'nil 'scheme 'common-lisp)

or

(let ((list (lambda (x) '(scheme)))) (car (list 'common-lisp)))

Luís said...

Anonymous's idea of exploiting lisp-1 vs lisp-2 is neat. We could shorten it to (let ((car (lambda (x) 'scheme))) (car '(common-lisp)))

John Cowan said...

R4RS was the first Scheme standard to require that #f and () were distinct in the sense of eq?, so Schemes older than that (and a few that claim to be Scheme/CL hybrids) do look like CL.

But this seems quite reliable to me:

(if (= -1 (let ((- +)) (- 3 4))) 'common-lisp 'scheme)

It depends on the fact that + and - are predefined in Scheme and also in CL as both variables and functions.

Joe Marshall said...

I tried exploiting the lisp-1 vs. lisp-2 idea as well and came up with this:
(let ((funcall (lambda (ignore) 'scheme)))
(funcall (lambda () 'common-lisp)))
but I like the other solutions as well.

Luís said...

How about adding elisp to the mix?

(let ((lisp 'common-lisp))
(let ((fn (lambda () lisp)))
(let ((funcall (lambda (ignore) 'scheme)))
(let ((lisp 'elisp))
(funcall fn)))))

Joe Marshall said...

Well done, sir.

Anonymous said...

Why does this work?
(if (= -1 (let ((- +)) (- 3 4))) 'common-lisp 'scheme)

Is it because of the lexical vs dynamic scope?
What is it about common-lisp that doesnt allow the redefining of functions like car, list, +, etc?
I thought all lisp languages had first class functions and that because of this we could redefine all functions. Is that wrong?

Also a side note: Good job to whoever made this website, theres almost no javascript on it. Usually when I try comment I have to enable 10 different things that my noscript plugin blocks, here I didnt need to do anything.

Joe Marshall said...

Both Common Lisp and Scheme are lexically scoped, but Common Lisp has separate namespaces for functions and variables, where Scheme uses one namespace for both. The LET form in Common Lisp binds only in the variable namespace and doesn't shadow function names, so when you write (let ((- +)) (- 3 4)), only the variable named "-" is bound, but the function reference in (- 3 4) still refers to the subtract function. In Scheme, with both functions and variables in one namespace, the "-" in (- 3 4) would be a reference to the LET bound variable and thus evaluate to the add function.

In this example we aren't redefining anything, we're making a new binding that temporarily shadows the global binding while we evaluate (- 3 4), but most Lisp systems will also have a way to redefine at least some of the built-in functions. This is usually not a good practice because it can affect the stability of the entire system if a library function depends on a global definition that you smash.

Most Common Lisps have some sort of way to "lock" the package system so that you don't accidentally overwrite a built-in function that might be used by a library function, but most Common Lisps will allow you to bypass the lock and redefine built-in functions. The effect of redefining a built-in depends on the implementation.

Scheme systems are a bit different. They usually go out of their way to ensure that the library functions have private copies of the built-in functions, so you can't easily redefine them. You can redefine your own copy if you want, though.

Glad you find the web site easy to use. I wish I could take credit for that, but I'm just using what Blogger provides.

Anonymous said...

Lambdas for the win! Here we define the function and call it immediately


((lambda () (if '() 'scheme 'common-lisp)))