Unorthodox opinions on computer science and programming.
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?
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
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.
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.
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.
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.
I was thinking something like this but apparently my Scheme is too old.
ReplyDelete*: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
A form that exemplifies the prissy sensibilities of scheme
ReplyDeleteJust quote nil
ReplyDelete(if 'nil 'scheme 'common-lisp)
or
(let ((list (lambda (x) '(scheme)))) (car (list 'common-lisp)))
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)))
ReplyDeleteR4RS 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.
ReplyDeleteBut 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.
I tried exploiting the lisp-1 vs. lisp-2 idea as well and came up with this:
ReplyDelete(let ((funcall (lambda (ignore) 'scheme)))
(funcall (lambda () 'common-lisp)))
but I like the other solutions as well.
How about adding elisp to the mix?
ReplyDelete(let ((lisp 'common-lisp))
(let ((fn (lambda () lisp)))
(let ((funcall (lambda (ignore) 'scheme)))
(let ((lisp 'elisp))
(funcall fn)))))
Well done, sir.
ReplyDeleteWhy does this work?
ReplyDelete(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.
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.
ReplyDeleteIn 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.
Lambdas for the win! Here we define the function and call it immediately
ReplyDelete((lambda () (if '() 'scheme 'common-lisp)))