D-Expressions: Lisp power, Dylan style [pdf]
people.csail.mit.edu
people.csail.mit.edu
But that’s probably optimizing for the wrong case. Macros are used much more often than they are written, and the person writing the macro should probably understand syntax and grammar. Moreover, as soon as you add guardrails like hygiene (like Scheme does), the inherent conceptual simplicity of Common Lisp style macros is greatly reduced.
Too bad Dylan never took off. I think it would have been a better language than Java for enterprise software. Ironically, it was a create of the time insofar as it focused on something (trying to match C for performance) that ultimately didn’t end up mattering so much. We now write everything in one of the worst and least optimizable languages this side of TCL, and that doesn’t hold back deployment.
Should that have been:
"... languages without either JIT or AOT on their reference implementation, are mostly [only?] suitable for scripting and teaching purposes."
... ?
Adam Sah, UCB: An Efficient Implementation of the Tcl Language (1994, UCB Masters Degree Thesis for Professor John Ousterhout):
https://www2.eecs.berkeley.edu/Pubs/TechRpts/1994/CSD-94-812...
After Ousterhout and his team went to Sun, but before the Java Juggernaut made its debut, Sun was positioning TCL to be the "Official Scripting Language of the World Wide Web".
Brian T. Lewis, Sun Microsystems Labs: An On-the-fly Bytecode Compiler for Tcl (1996, Usenix TCL/Tk Workshop):
https://www.usenix.org/legacy/publications/library/proceedin...
I wonder what would have happened if John Oosterhout's TCL team had applied Dave Ungar's Self team's JIT tech to TCL, before the Self team left Sun and made HotSpot (who Sun then hired back to apply to Java). Anyone know if / how those two teams at Sun overlapped / interacted at Sun Labs?
Then "The TCL War" happened, which didn't help TCL's world domination plans either:
https://vanderburg.org/old_pages/Tcl/war/
https://news.ycombinator.com/item?id=12025218
Slightly Skeptical View on John K. Ousterhout and Tcl:
https://softpanorama.org/People/Ousterhout/index.shtml
>There was some concerns about TK future. See for example the following message from [Python-Dev]
>FYI ajuba solutions (formerly scriptics) acquired by interwoven
On Tue, Oct 24, 2000 at 07:07:12PM +0200, Fredrik Lundh wrote:
>I'm waiting for the Tcl/Tk developers to grow up -- they still
>fear that any attempt to make it easier to use Tk from other
>languages would be to "give up the crown jewels" :-(
>In the long run this has probably harmed Tk seriously. If Tk was just a widget set divorced from Tcl, then it might have been chosen as the underlying widget set for GNOME or KDE, and then have benefited from the development work done for those projects, such as the GNOME canvas enhancements, which now can't be absorbed back into Tk without a lot of effort to merge the two sets of code.The genius of TCL/Tk:
https://news.ycombinator.com/item?id=22709478
DonHopkins on March 28, 2020 | parent | context | favorite | on: Is there any code in Firefox (as of 2020) that com...
The genius of TCL/Tk, and the reason I believe Tk was so incredibly successful despite the flaws and shortcomings of TCL, is that toolkits like Motif, based on the X Toolkit Intrinsics, that aren't written AROUND an existing extension language, end up getting fucked by Greenspun's Tenth Rule:
https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
>"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp." -Philip Greenspun's Tenth Rule
The X Toolkit ends up needing to do all of these dynamic-scripting-language-like things, like resolving names and paths, binding events to handlers, calculating expressions, and instantiating objects based on resource data. So if it doesn't already start out with a standardized scripting language to use for that, it has to duplicate all that dynamic almost-but-not-quite-entirely-unlike-half-of-Common-Lisp stuff itself.
Case in point: Motif's infamous UIL (because X resource files weren't half-assed enough).
https://www.donhopkins.com/home/catalog/unix-haters/hpox/uil...
And then when you do get around to plugging that toolkit into some other scripting language (the way WINTERP plugged Motif/Xt into XLisp, or GTK/GObject plugs into Python for that matter), now you have two or more fat bloated complex buggy poorly documented incompatible impedance-mismatched half-assed competing layers of middleware and object models tripping over each other's feet and spraying each other with seltzer bottles like the Three Stooges.
https://www.youtube.com/watch?v=VO9RP4QEZKU
https://news.ycombinator.com/item?id=22610342
>Speaking of the plague, does it support UIL? ;)
>Neils Mayer described WINTERP (XLisp + Motif) as: You might think of such functionality as "client-side NeWS without the postscript imaging model".
http://nielsmayer.com/winterp/
>Don Hopkins wrote to a comp.human-factors discussion about "Info on UIL (User Interface Language)" on January 22, 1993:
https://groups.google.com/d/msg/comp.human-factors/R3wfh90HM...
>Here are some classic messages about UIL. Avoid it like the plague.
[...]
What makes Lisp macros so intuitive is not the prefix notation, it's the homoiconicity of the language. Writing a macro is just a list operation, where the familiar map / filter / reduce applies.
Subjective, I know, but I think (+ 1 2 3 4) is way nicer than (1 + 2 + 3 + 4).
4 3 2 1 + + +
If I have to write 1 + 2 * 5 - 3, I'm not going to "start" with *.
I will type:
(+ 1 2<-<-(* ^E 5) <-<-<-<-<-<-<-(- ^E 3))
It indicative of how I'm thinking about it (regardless of how the navigation is done).I just have a lot of those starts and stops when converting infix to prefix in my tiny Piglet brain.
That said, as a general rule, yea, I like (+ 1 2 3 4) and its ilk. It takes a bit of exploration to read. For example, I need to transit the entire list of 1 2 3 4 to understand all that is being added, which can be more difficult with longer expressions. But that can be mitigated with formatting:
(+ (this that)
(func zzz)
(if (< v q) 1 3))
(Function conditions are also fun to intermix into things!)I think just as a rule, I find formatting more important in general with s-expr systems than algolesque systems.
That's a great example of an expression that can be ambiguous, but not with prefix/suffix notation.
Most people that write lisp-like code use an editor that helps with s-exp editing (like paredit), so that isn't really a significant issue. In fact, I think it is faster to write s-exp code than algol-likes once you've become accustomed.
I agree that formatting is very important w/ parens langs, but there are many languages that consider formatting a high concern.
I would also argue that reading code is harder than writing it. Optimizations that speed up writing code is less interesting to me than ones the help reading/understanding/making assertions easier about code.
Finally, infix notation is available in lisp/scheme, it's just a macro away (but seriously, don't).
When using "curly brace languages", what I really miss is structural editing. I often deal with tree-like structures in any language, and being unable to simply cut a node, move my cursor to the start/end of a node, nest something, etc. is really inconvenient. These are operations that take me at most a second when using Emacs with smartparens. In JSX, for example, these one-second operations need me to imitate smartparens' behavior by hand then run Prettier since indenting by hand is an even bigger waste of time. Transforming e.g.
<Foo>
<Bar>
<Baz />
</Bar>
</Foo>
to <Foo>
<Bar />
<Baz />
</Foo>
takes *seconds* even though the equivalent operation with S-expressions is a single keybind in Emacs.The numerical operators, with the Boolean operators, negation, comparisons, maybe implicit type conversions as well. Maybe && as separate from &. Maybe user defined operators with different precedence to the builtin ones.
Maybe the large precedence table you have to keep in working memory to use the infix conventions is still simpler. Maybe even when there's a style guide saying to put lots of parentheses in as well. It starts to be difficult to fit a definition of simple to the observations though.
Mine, neither, for maths.
But for everything else, we’re both already used to prefix:
fprintf(STDOUT, "foo %d", 3);
is prefix, just as much as this is: (format *standard-output* "foo ~d" 3)
And it turns out that the advantages of a single homoiconic notation are so compelling that it’s worth a little bit of mild ugliness when dealing with maths.Now if we want to handle compounded expression, you need either a given constant finite number of argument, or have a reserved word to mark grouping, much like `)`. A bit like stack based programming languages.
I keep trying to pin point the psychology of it. Cause even as a kid, I was more attracted toward HP calcs even though I never heard of lisp at this point, but RPL felt like infinite magic. And of course when I ran into emacs, I felt the same strange feeling.. there's something.
target.method(argument, another)
Here, the operation is `method` and the operands are `target`, `argument`, and `another. Note that the operation appears in the middle of the operands.Even then, it's still not a prefix operation. It's postfix.
Things like this are mentioned, in some detail, in the O'Reilly book Python in a Nutshell, IIRC, in the first edition, which is what I have.
Couldn't quite wrap my head around all the material in that chapter, TBH.
But I still like to read about such stuff, and I do understand bits and pieces of it.
Note that the "(" in (a, b) is also an operator. (There is some special parsing because otherwise you could do things like args = (1, 2, b=3); target.method args.)
As to whether <thing from target.method><thing from (a, b)> is postfix, I'm not sure how you get there. Yes, there's an implicit funcall at the end, but there's something similar with (+ a b) or ((. target method) (arglist a b)) and we wouldn't call them postfix.
var x = 123
print(-x.abs())
Or even: print(-123.abs())
What do those print? Do they print the same thing?The culture is the ultimate "reality distortion field." Prefix notation would be seen as intuitive, if that were culturally established. We'd see something like PEMDAS as arbitrary and silly.
Just look at how much content there is around PEMDAS and interpretation of math problems. Clearly, it really isn't "intuitive." We just have this enshrined in the culture. (That said, one of the biggest UX mistakes the designers of Smalltalk made, was to eschew operator precedence!)
Yeah, and IMHO most operations in most languages suffer from being mostly prefix. Yes, LISP is not that much worse, but doubling down on the bad part doesn't exactly recommend it. ¯\_(ツ)_/¯
One of the cool things about Smalltalk is that it consistently makes everything infix.
I think that most problematic for prefix notation are not arithmetic operations, but field accessors.
In C you have a->b->c->d, in Lisp it would be (d (c (b a))), which makes you to jump to the center and read it from the inside-out.
The direct translation of the Lisp expression you shared to C would be: d(c(b(a))), which just like the Lisp expression, evaluates from the middle out. Both are fine to read from left to right though.
Not really. -> looks like binary infix operator, but it is really an unary postfix operator (parametrized by field). Because field itself is not a first-class entity in C.
#include <stdio.h>
#include <stdlib.h>
struct foo {
int a;
};
int main() {
struct foo *f = malloc(sizeof(struct foo));
f->a = 5;
printf("%d\n", f->b);
free(f);
return 0;
}
test.c:11:21: error: no member named 'b' in 'struct foo'
11 | printf("%d\n", f->b);
| ~ ^
1 error generated.
What do you mean when you say fields are not first class in C?Javascript?
If so, why do you say it's a least optimizable language?
Do you mean for ahead-of-time single point in time static compilation, rather than code evolving within a JIT afforded real runtime statistics?
I don't have numbers but as an old optimizing compiler person I'm asking because it's plausible you know datasets i have not seen.
https://niklas-heer.github.io/speed-comparison/
Honestly though, you could probably take your pick. Javascript is suprisingly fast for such a dynamic intepretted language, but PHP, Python and Ruby are all simultaneously some of the most used languages and the slowest.
Your argument seems to be that Macros ought to be harder to write in exchange for being easier to use? What non-lisp macros are easier to use than lisp macros, and how?
Ideally that has no impact on how easy it is to use a macro, except to the extent a bug would make the macro hard to use.
Clojure offers a nice middle-ground to this. In Clojure, symbols are namespaced, and syntax quote "fully qualifies" symbols. A "fully qualified" symbol is a symbol with its namespace explicitly written out. For instance, foo is unqualified whereas clj.user/foo is fully qualified. The language disallows binding fully qualified symbols. So the most common bugs arising in unhygienic macros are eliminated while maintaining the same level of "simplicity" as macros in Common Lisp.
There are also other features to help ensure hygiene, such as syntax that automatically produces gensyms for you. e.g. instead of
(let [foo-sym (gensym)]
`(let [~foo-sym some-value]
(do something with ~foo-sym))))
one can simply write `(let [foo# some-value]
(do something with foo#))It's a bit type-cast as a language for numerics and scientific programming, a niche where it's enjoying robust success.
But as a language, it's fully suited to general purpose programming, providing an excellent experience in fact. The ecosystem for most applications which aren't in the existing niche is somewhat thin, but that's a chicken-and-egg problem. It has solid package management, a good concurrency story, well-designed FFI, and performance-sensitive parts of a program can be honed to native speed by making them type-stable.
Slept on imho.
I am telling you, Mulisp (1978) solved all problems, because everything could be pretty-printed either in Algol-style or Lisp-style.
Also, Mathematica since forever (maybe version 1.0 in 1988) has had things like `CForm` and `FortranForm`.
Its parser was a separate layer from its compiler, so Dan Bornstein (one of the ScriptX designers who later made Dalvik for Android) write a Scheme parser front end for it.
ScriptX influenced MaxScript, the scripting language in 3D Studio Max, which was written by one of the ScriptX designers, John Wainwright. Other Kaleidan Lisp hackers include Shell Kaplan (Employee #1 at Amazon) and Eric Benson (who worked on Lucid Emacs), both went to Amazon and did a lot of Lisp and Lisp inspired stuff there.
https://en.wikipedia.org/wiki/ScriptX
Shel and others wrote about Lisp at Amazon and their Lisp-inspired templating notation here:
https://news.ycombinator.com/item?id=12437483
Kaleida's ScriptX training classes were lots of fun: taught by Randy Nelson, who is a professional juggler and former member of The Flying Karamazov Brothers, who Steve Jobs hired to teach developers at NeXT and Apple:
https://web.archive.org/web/20190310081302/https://www.cake....
https://news.ycombinator.com/item?id=18772263
I used John Wainwright's MaxScript plugin API to integrate the C++ character animation system code I wrote for The Sims into 3D Studio Max, to make an animation content management system and exporter in MaxScript, which is like Lisp without parens for 3D:
https://web.archive.org/web/20080224054735/http://www.donhop...
Dan Ingals's work on Fabrik inspired a lot of the stuff I did with ScriptX at Kaleida:
https://news.ycombinator.com/item?id=29094633
Apple also developed Sk8, which was a lot like Dylan and ScriptX, i.e. Lisp without all the parens, plus objects.
https://news.ycombinator.com/item?id=38768635
Mike Levins explained Coral Common Lisp and Dylan and Newton and Sk8 and HyperCard in the broader context and palace intrigue of Apple:
https://news.ycombinator.com/item?id=21846706
mikelevins on Dec 20, 2019 | parent | context | favorite | on: Interface Builder's Alternative Lisp Timeline (201...
Dylan (originally called Ralph) was basically Scheme plus a subset of CLOS. It also had some features meant to make it easier to generate small, fast artifacts--for example, it had a module system, and separately-compiled libraries, and a concept of "sealing" by which you could promise the compiler that certain things in the library would not change at runtime, so that certain kinds of optimizations could safely be performed.
Lisp and Smalltalk were indeed used by a bunch of people at Apple at that time, mostly in the Advanced Technology Group. In fact, the reason Dylan existed was that ATG was looking for a Lisp-like or Smalltalk-like language they could use for prototyping. There was a perception that anything produced by ATG would probably have to be rewritten from scratch in C, and that created a barrier to adoption. ATG wanted to be able to produce artifacts that the rest of the company would be comfortable shipping in products, without giving up the advantages of Lisp and Smalltalk. Dylan was designed to those requirements.
It was designed by Apple Cambridge, which was populated by programmers from Coral Software. Coral had created Coral Common Lisp, which later became Macintosh Common Lisp, and, still later, evolved into Clozure Common Lisp. Coral Lisp was very small for a Common Lisp implementation and fast. It had great support for the Mac Toolbox, all of which undoubtedly influenced Apple's decision to buy Coral.
Newton used the new language to write the initial OS for its novel mobile computer platform, but John Scully told them to knock it off and rewrite it in C++. There's all sorts of gossipy stuff about that sequence of events, but I don't know enough facts to tell those stories. The switch to C++ wasn't because Dylan software couldn't run in 640K, though; it ran fine. I had it running on Newton hardware every day for a couple of years.
Alan Kay was around Apple then, and seemed to be interested in pretty much everything.
Larry Tesler was in charge of the Newton group when I joined. After Scully told Larry to make the Newton team rewrite their OS in C++, Larry asked me and a couple of other Lisp hackers to "see what we could do" with Dylan on the Newton. We wrote an OS. It worked pretty well, but Apple was always going to ship the C++ OS that Scully ordered.
Larry joined our team as a programmer for the first six weeks. I found him great to work with. He had a six-week sabbatical coming when Scully ordered the rewrite, so Larry took his sabbatical with us, writing code for our experimental Lisp OS.
Apple built a bunch of other interesting stuff in Lisp, including SK8. SK8 was a radical application builder that has been described as "HyperCard on Steroids". It was much more flexible and powerful than either HyperCard or Interface Builder, but Apple never figured out what to do with it. Heck, Apple couldn't figure out what to do with HyperCard, either.
We Smalltalkers were discussing doing this at Camp Smalltalks in the 2000's.
I'm currently working in golang, and I've noticed that Goland IDE expends quite a bit of compute indexing and parsing source files. Not only that, but, a significant portion of the bug fixes have to do with this, and the primary motivation for restarting Goland has to do with stale indexing.
Wouldn't tools like git simply work better, if they were working off of some kind of direct representation of the semantic programming language structures? Merging could become 100% accurate, for one thing. (It's not for some edge cases, though many might mistakenly think otherwise.)
How so? Merge conflicts don't arise from the inability to locate the proper change, but from the inability to decide, which of the change, if any, would be proper.
But when Mulisp finally conquers the world, we can make backquote-style macro for Algol, where special characters indicate that following stuff needs to evaluated and inserted to the code.
That will be never. If Mulisp hasn't done so in the last 46 years, it never will.
https://web.archive.org/web/20140530042250/http://maben.home...
My one qualm is that it does not elaborate on implementation as much as I'd like.
(defun bresenham (a x0 y0 x1 y1 &aux dx dy d y)
#◇dx:x1-x0,
dy:y1-y0,
d:2*dy-dx,
y=y0◇
(loop for x from x0 to x1
do #◇a[x,y]:1,
if d>0 then
(y:y+1,
d:d-2*dx),
d:d+2*dy◇))
you can make the code above fully operational in common lisp using dispatch macro on a unicode character, so i've been experimenting with such infix mode in my private code. i'll leave the judgement over whether or not this increases readibility.