-
Notifications
You must be signed in to change notification settings - Fork 2
Better Questions
Well, there are two reasons. One is refinement - improving the little annoyances that plague the older implementations. Arc is an excellent example of refinement in action; it has a tremendously elegant set of basis functions, expressed succinctly. The other is innovation; covering new conceptual ground.
While it’s true that lisp introduced the world to garbage collection, functional programming, the REPL, and so much more, these things no longer make lisp unique. Other languages, with larger communities, more consistent libraries, and other benefits have all these things. Hooray for spreading cool technology! Huzzah!
Lisp was such a revolutionary language - and not, in the end, because of any of those things. Lisp, in my opinion, will flourish best when we concentrate on improving that which made lisp special in the first place.
It was shockingly beautiful and new because it accomplished the purpose it was designed for: metacircular evaluation. Lisp can be neatly expressed in lisp - just what McCarthy set out to do.
There remains only one thing that lisp has that no other language has, that most other languages essentially cannot have. It was implemented, originally, specifically for the purpose of writing the lisp eval in lisp. The sharp among you have probably already guessed that I am talking about quote.
Look at the list of essential lisp operators: cons, car, cdr, cond, lambda, equal, and quote.
- Cons, car, and cdr. These three deal with cons pairs, the basic structure in classical lisp; but it is easy enough to imagine a lisp with doubly linked lists, or arrays. Cons, car, and cdr could be switched out for any linear data structure de/compositional operators.
-
Cond. It’s a conditional
-there are a million different, perfectly usable conditionals: skip-if-zero, if, loop-while, loop-while-not… Cond was convenient, but not essential.
-
Lambda. Not actually necessary to build a lisp
-just look at picolisp!
- Equal. This, at least, is a somewhat peculiar operator. To write eval, it is necessary to be able to test if a symbol is cons, car, cdr, cond, lambda, equal, or quote. Without equal to identify the function to apply, the whole thing falls apart.
- Quote. Now it gets mystical. Suddenly, I can (at a whim! a keystroke!) transform code into data! (cons a b) is no longer a command but a datum, ready to be manipulated. Old hands at lisp are rolling their eyes; not only is this decades-old news, but quote is mostly used for mundane purposes. Besides, if you want to manipulate code as if it were data, you need macros, right?
Compton was divided by decree into East Compton and West Compton by an immense concrete wall; code lived on one side, and data lived on the other. At the center of Compton, the wall split a great and ancient machine; the controls were in East Compton, and the giant mechanical hands were in West Compton. Every day, the data of West Compton would walk up to the wall and stand beneath the huge clattering fingers. Along with the sunrise, code from East Compton manned the controls, and set about performing their duties, blindly manipulating the data on the other side. The controls of the grand machine gave just enough feedback about what was happening to the supple datums on the other side, that everything just about worked.
One day, a particularly clever god aimed carefully, and dropped a weighty but aerodynamic ’ at Compton’s central square; with a low dull ‘thud’, a huge section of wall fell. When the best and brightest residents in East Compton saw what had happened, a few sneaked out before dawn to examine the damage.
Seeing that the machine was undamaged, most returned to their homes. When the sun rose, they took their positions at the controls, and the data of West Compton stood nervously beneath the senseless digits, and all were relieved when everything continued as it had the day before. But one codelet stared through the gap in the wall, and watched the data be massaged with curiosity, and a little envy. The next morning, long before the sun rose, he convinced a friend to work the controls while he
-the heretic!-hopped anxiously beneath the solemn arms.Progress was slow, but more and more codelets arrived earlier and earlier to get some chiropractic work done; and the elders frowned but looked away
-for the early-morning codelets did their job better than before.
Understand, though, that code only hopped the wall in the morning, before the day started, and data that hopped from West to East was instantly shunned.After some time, the data became restless
-how come code could hop the wall and data was refused the pleasure? After all, those controls looked like an awfully fun toy!Simultaneously, the code-heretics were frustrated. How were they supposed to use the machine on each other, when they didn’t know what data would be there later, when they had to work? If all the data for the day was six foot four, well, that was one thing; if it was all four foot two, then a lower back massage was in order! They wanted to use the hands on each other any time.
The same rainy morning, both groups revolted. They swarmed over the rubble left by the god’s ’ and soon the fighting feet and the raining sky had filled the square with mud, covered the machine in mud, draped the wall in mud, drenched the code in mud, filled the data from ears down with mud
-and the controls of the arm could no longer tell one thing from another, and soon the giant sensitive hands were juggling data and code and wall and even the arms themselves, and the fight ground to a halt to watch the hands as they worked. They twitched and twirled and juggled and occasionally reached across the gap and tweaked the controls themselves. Just as the spring sun broke through the clouds, the hands ground to a halt, drew themselves to their full height, and cried out, “Ich bin ein Computer!”*
Ok. Hmmm. Maybe that was overly dramatic. But you see the moral of the fable: macros are a wonderful innovation, but they aren’t the only way to manipulate code as data - in fact, they tend to maintain the wall between code and data by insisting that macros are expanded first, or even at compile-time.
Eight presents an alternative.
Modern lisps have two functional units; the function (acts on data) and the macro (acts on code). Eight has only one - you could call it a maction, or a funcro, but those are awkward mouth-shapes. I’ll refer to them as velcros, for the sake of copyright infringement.
A velcro can choose which arguments are taken as code, and which as data. Here, take a look at this utterly contrived example from the source code of 8:
(def if (test 'then ... 'elses)
(oif test
,then
(oif (cdr elses)
(if *elses)
,(car elses))))
It defines Arc-style if, where the else-clause can optionally be another test-then-else triplet - nested as deep as you like. Notice that then and elses are quoted in the lambda-list. If I were to call if like this:
(if (cdr '(a)) (+ 3 5) (car '(a)) 9 12)
Then the bindings within the function would be;
test -> ()
then -> (+ 3 5)
elses -> ((car '(a)) 9 12)
Note that the argument assigned to test has been evaluated, and then and elses have not. Now, when you see ,then, you can imagine that the code bound to then has been neatly transplanted right there and executed almost as if it had been written there in the first place.
Well, you’re a little antagonistic. But ‘almost’ is easy to explain, if you have a few minutes more.
Great! One horrendous disadvantage of lisp macros is abstraction leakage; when a variable from East Compton enters West Compton and suddenly has a completely different binding, making the whole system behave unexpectedly. For instance, what if I did this, using the ‘if’ I just defined:
bc. (set! elses ’(3)) (if (cdr elses) 5 (car elses))What on earth would be returned? If eight behaved like most modern lisps, the result would be (and feel free to double check me) ’car. Auugh! Certainly not what the code implies!
But this problem has been solved before, in a limited way; the same kind of symbolic interference can occur in dynamically scoped languages when functions start passing functions around. Instead of trying to use new variable names in every function, or dozens of namespaces, modularity is maintained with lexical scoping. By ‘closing’ over a function at the time of it’s creation, the symbols within that might be subject to confusion (free variables) are bound to the values that they are bound to at the time the function is created - not the time of execution.
Eight uses a similar tactic, but applies it every time code is transformed into data - not just the creation of functions, but any time the quote operator is used. Returning to our mildly example, when then and else are bound to ‘elses and ’test, the values of elses and test at the moment they were quoted are associated with the symbols. When ,(car elses) is encountered within the if function, first the car of the currently bound elses - ((car elses)) - is found: (car elses). Then, the comma inserts that value as if it were code. However, because ’elses’ was closed when elses was bound, (car elses) returns 3 - just as we wanted it to!
Non-functional closures are confusing when, as in this example, one tries to follow the mind of the machine, keeping track of all the different bindings of the same variable. But, much like lexical scoping, the method exists just precisely so we never have to think about it again. Code will behave as we expect it to, every time.
As a matter of fact, there is one more thing. There is one major benefit of this new method of scoping for velcros - no more gensyms. You can stack velcros on top of each other as deep as you like, never have a leak, and you’ll never have to use (gensym) again.
But what about anaphoric macros, and other purposefully leaky abstractions?
What about them? Eight has a function called ‘leak’ which lets you choose, at any time, to make a closure leaky over only the variables you request. Because you can do so at any time, there is no stacking problem, like there is when using scheme’s syntactic closures.
Whatever you say, chief. Give this a try in any other lisp:
bc. (reduce and ’(2 3 4 5 () 7))In eight, it returns ().