Tuesday, March 29, 2011

Syntax puns considered harmful (well, not really)

Schemers use the word "syntax" to mean at least two (and maybe even three) different things:
  • in DEFINE-SYNTAX and LET-SYNTAX, syntax means macro expander
  • in SYNTAX and QUASISYNTAX, syntax means code
  • in BEGIN-FOR-SYNTAX, syntax means compile-time (although it's debatable whether this use of syntax is really different from the first one)
There is a certain elegance in that, but in the end it's also a bit silly.

And it's problematic, as the (awesome) paper Languages as Libraries shows: because LET-SYNTAX is already taken to mean "locally bind a macro expander", they have to use WITH-SYNTAX to mean "locally bind a piece of code".

Monday, March 28, 2011

Don't diss Java - Be Happy


On the occasion of Gosling joining Google, it's Java diss time again. But most Java criticism doesn't take its raison d'être into account:
We were after the C++ programmers. We managed to drag a lot of them about halfway to Lisp. Aren't you happy? — Guy Steele
Java has many faults. But most of them are there by design. That's not something many language designers can claim.

Java was a Trojan Horse, designed to slip in a modicum of dynamic features without C++'ers getting suspicious.

That whole dynamic-language-in-the-mainstream thing may have never happened without Java.

Aren't you happy?

Computational Trinitarianism

Given that software is so utterly confusing, it's often helpful to bring in metaphors from other areas, Christianity in this case: The Holy Trinity from Bob Harper's excellent Existential Type:
The Christian doctrine of trinitarianism states that there is one God that is manifest in three persons, the Father, the Son, and the Holy Spirit, who together form the Holy Trinity. The doctrine of computational trinitarianism holds that computation manifests itself in three forms: proofs of propositions, programs of a type, and mappings between structures. These three aspects give rise to three sects of worship: Logic, which gives primacy to proofs and propositions; Languages, which gives primacy to programs and types; Categories, which gives primacy to mappings and structures. The central dogma of computational trinitarianism holds that Logic, Languages, and Categories are but three manifestations of one divine notion of computation. There is no preferred route to enlightenment: each aspect provides insights that comprise the experience of computation in our lives.

OOP, again and again and again

Nothing gets LtU's juices flowing like a good old OOP discussion. It starts with a harmless "OO is anti-modular? Really?" and runs into the right margin very quickly. It's worth a read.

In that thread there's also a quote describing OOP that's very close to my POV, by Jonathan Aldrich:
building dynamic dispatch into the language ... is the one thing that separates languages commonly considered "OO" from those that are not

Monday, March 7, 2011

That explains it

I certainly sat in lots of Scheme design meetings where I pushed for obvious things like branch cuts, error handling, and other "useful" things and was told that such things would ... make the language "too useful" [thus meaning too many people would flock to it and it would be the end of the designers' ability to do the things to the language that _they_ wanted to do].
Seriously though, this shows that the forces behind Scheme are far from obvious.

Understanding Hygiene (part 2) -- SRFI 72

My critically acclaimed Understanding Hygiene (part 1) contains the absolute baseline necessary for understanding hygienic macros.

This post is about my understanding of André van Tonder's SRFI 72 - a specific way of implementing hygiene, which differs from more widely used hygienic systems such as the venerable syntax-case.

It must be said that hygiene is still an area of active research. There be dragons. Nevertheless, van Tonder is quite confident of his design, going so far as calling it "improved hygiene". David Herman, who wrote the bookdissertation on the theory of hygienic macros is skeptical. Anton van Straaten makes some mediatory points. Matthew Flatt raises even more mind-bending questions.

That said, for a hygiene noob like me, SRFI 72 seems to be an elegant and workable design. I wrote a half-assed Lisp->C compiler that contains a SRFI 72 inspired macro system (here's a test from SRFI 72 in my Lisp that works -- phew) and the experience was quite pleasurable. Of the couple thousand lines of C in the system, only a couple dozen or so deal with hygiene.

Basically, what SRFI 72 says is this: when you create a piece of code using the code constructors syntax or quasisyntax, e.g.
(syntax (let ((x 1)) (foo x)))
the identifiers in there (let, x, foo) get a fresh hygiene context (or "color"). Thus they won't conflict with identifiers of another color -- such as those created by somebody else (e.g. another macro, or the user's source). That was the easy part.

Additionally, SRFI 72 makes such pieces of code obey a discipline that is similar to lexical scoping. That is the meaning of 72's money quote that van Tonder repeats multiple times, in the hope of hammering his point home:
the evaluation of any nested, unquoted syntax or quasisyntax forms counts as part of the evaluation of an enclosing quasisyntax.
Say we have some piece of silly code, like:
(quasisyntax (bla bla bla ,(some-unquoted-stuff (lambda () (quasisyntax (bla bla bla))))))
The outer quasisyntax constructs/quotes the piece of code. Then the "," unquotes and we call some contrived function that happens to take a lambda as argument. Then, inside the lambda there's another quasisyntax, so we're quoted again. The contrived unquoted stuff and the lambda don't matter. What matters is that there's an inner quasisyntax lexically embedded in the outer quasisyntax.

Now, SRFI 72's money quote tells us that the inner quasisyntax's bla's have the same color as the outer quasisyntax's bla's. Why? Because the inner quasisyntax is lexically nested in the outer quasisyntax, and like a lexical variable binding, the outer quasisyntax's color is "inherited" down to the inner quasisyntax.

This took me a looooooooooooooooooong while to figure out, and I hope that this post will help those trying to understand SRFI 72.

Questions?

Tuesday, March 1, 2011

Language design criticism = art criticism (?)

A comment by Sean McDirmid on LtU is spinning off into a nice PL design discussion:
We should only critique designs like we critique art, I like this and I don't like that, made from the perspective of another person's tastes. C++ is an example of a language that has a controversial rather than poor design, some people find it ugly while others find it beautiful. A good design is simply a language designed by a good designer, and it might not suit your tastes but you should appreciate it for what it is, right?

I got into some arguments recently over Scala. That I was critical of Scala and compared it to C++ led someone to think I was a Scala hater, while nothing could be farther from the truth. People tend to get very emotional about their languages (or paradigm) of choice. A good discussion involves acknowledging our biases and being objective about our and others' opinions.

Saturday, February 26, 2011

Applied Types

Chris Double is again exploring ATS, a language with a very advanced type system:
ATS did save me from some errors in my larger program that this snippet can be used to demonstrate:
  • The read call must not read more data than the size of the buffer allows.
  • The conversion to a C string in-place means we need to read one less than the size of the buffer.
  • The file must be closed after use.

All these issues resulted in compile time errors. [!]

Thursday, February 24, 2011

Emscripten

This is CPython, the standard Python implementation, compiled from C to JavaScript using Emscripten, running in your browser (without any plugins).
Highly sick.
Emscripten is an LLVM-to-JavaScript compiler. It takes LLVM bitcode (which can be generated from C/C++, using llvm-gcc or clang, or any other language that can be converted into LLVM) and compiles that into JavaScript, which can be run on the web (or anywhere else JavaScript can run).
[So will we run Emacs in X11 in the browser??]

Saturday, February 19, 2011

Wittgenstein for programmers

Harrison Ainsworth's Wittgenstein for programmers is an awesome project. And very quotable:
  • Where a proposition describes the world, a program constructs the imagination.
  • The limits of our programming-languages mean the limits of our imagination.
  • What can be designed at all can be designed precisely. What is unknown we must leave uncoded.

Browsers will let PLs break free from plain text

Programs really aren't plain text. Or they shouldn't be.

But until now there was no way out. OSes come with Ed, and Ed is the standard text editor.

But pretty soon, we'll want to do development in our browsers.

Do we really want to port our 1960's plain text infrastructure to browsers? Surely the answer must be a resounding ``No!''

Reinventing integrated development environments in the browser will make it possible to ditch plain text, and switch to the multimedial representations our programs deserve.

Finally.

Wednesday, February 16, 2011

Free as in Freedom

Vladimir Sedach just tweeted something dear to my heart:
Arguing that the #GPL is restricting your freedom is exactly the same as arguing that the 13th Amendment is restricting your freedom.
Go GNU!

BTW, I'm now also a twit at @msimoni and like to get in touch with all PL enthusiasts out there!

Dear Lazyweb: RMS on namespace management

I remember reading an article about RMS some time ago, where he says that a module system/namespace management for Emacs Lisp is not something he finds important.

I find this interesting because Linus essentially said the same thing wrt C.

If you know which article this is, please be so kind and post it in the comments, or send me a mail. Thanks!

Friday, January 28, 2011

The LISP עץ החיים



Plain awesome!

(cached copy in case the original site goes down)

Wednesday, January 26, 2011

Proglangs are machines, not media



I've blogged about this before, but I think it's worth stressing again:

Programming languages should not be viewed as media, but as machines.

I first had this intuition when learning Common Lisp: CL is like a big program (i.e. an object, a machine), that's configured and used through Lisp code.

This has the nice side-effect of vindicating Lisp's built-in uglyness: who cares what a machine looks like, as long as it's the best for the job?

(Thanks to Harrison Ainsworth for the initial inspiration.)

[Edit: Ah well, I guess I pushed the publish button too quick. Of course, PLs are media. But that's obvious. I think it's important to also look at their object, machine aspects.]

Sunday, January 23, 2011

Maniacal, religious dedication to code beauty

It's hard to keep source code "beautiful".

I think the only way it can work is to be dedicated like Zen monks cleaning their monastery - even one speck of dreck is too much.

The best example I know is Jonathan Bachrach's GOO (which also has a lot of other qualities). The code in there is phenomenally clean and consistent, and looks funny and artful to boot.

Thursday, January 20, 2011

The dark, dark, dark art of unwinding the stack

Ian Lance Taylor, the author of the gold linker and the GCC version of the Go language, has written a nice mini-series on how to unwind the stack:
This is wizard-level material, and it probably helps to have the ELF and DWARF specs handy.

Sunday, January 9, 2011

A Lisp slogan

Lisp is the worst programming language, except all the others that have been tried.

Thursday, January 6, 2011

Curing PL Anxiety

Some years ago I was in a state that I now discover in many other people on the web, and which I term "PL anxiety". It is characterized by a constant insecurity about which PL is the best, which one to learn, how "fast" each one is, whether PG is really right, etc etc.

In retrospective, my way out of this was a four-pronged approach:

(1) The Breadwinner PL

You should know one language that's reasonably popular where you live, and which will always land you a job. In the past that meant C++, C, Java, C#, and maybe Perl and PHP, but these days you can probably also get away with Python, Ruby, JavaScript, or Scala.

What's important about the breadwinner PL is that you know its semantics and its standard library by heart.

(2) The C PL

Infinitely many good things come to you by learning C, because your OS is written in it. Studying C, you'll learn awesome stuff, like how to handle SIGSEGV, exploit your branch predictor, write a language runtime, and what a linker is, for example.

The more you learn about C, the more you'll learn about a wide array of services that are already provided to you by your OS and compiler. And C is fun and simple to boot, and will give you the warm fuzzy feeling that comes from building things (almost) from the ground up.

(3) The Romantic PLs

These are languages that you really like and if you're really lucky, might one day get paid for using. You don't have to make a choice - learn them all. They all have their pros and cons, and in the end you'll have to roll your own, anyhow.

(4) The Other Interesting PLs Out There

Also keep an eye on olden golden PLs and newcomers to the scene, even if you're never going to use them: they may blow your mind, and that's what you really should be looking for. Ωmega is a good example.

The Real World

I've come to start new non-hobby projects in my breadwinner language, because it's the most convenient, I know it by heart, all my problems have already been encountered and hopefully solved by somebody else, there are tons of libraries, and PLs seem to matter very little for many projects (and if they matter, you can always Greenspun it).

Motivation

The thing that keeps me motivated learning more about PLs are PLs themselves. I learned C writing Lisp->C compilers, for example. I'm learning about dependent types because I'd like to implement a PL that has them. It's weird, but works for me.

HTH.

On Python and Ruby

I often like to make snide remarks at Python and Ruby, because ... well, because they're not Lisp.

To offset this a bit I'd like to say what I find good and impressive about them:

Python seems to be a great language for describing algorithms. For example, Kragen's hacks abound with samples of Python code that is simply wonderful to read, and seems like exactly the way to go.

Ruby is to be congratulated for demonstrating once and for all that dynamically-typed, semi-functional, object-oriented programming is a fun and useful paradigm for systems scripting.

Sunday, January 2, 2011

Why Lisp is a Big Hack (And Haskell is Doomed to Succeed)

2013 Update: I was young and stupid when I wrote this.

I fear that Haskell is doomed to succeed. — Tony Hoare
I ♥ Lisp. I think it's the best tool we have, at the moment, for many applications.

I don't really love Haskell that much, but I track its progress with awe. (When I say Haskell, I'm not only speaking about Haskell per se, but also about all the FP languages in its halo, like Ωmega, Agda, Epigram, ...)

And when I look at Haskell, it seems obvious to me that it's out to eat Lisp's lunch. In fact, eat all other languages' lunches.

The gist of this post is: In the not-so-far future, Haskell will be able to do everything Lisp can do now (and more), but in an adjustably-safe, statically-verified manner.

What do I mean by that?

Adjustably-safe: In this mythical, not yet-existing, but clearly on-the-horizon "Haskell", you'll be able to choose how much safety you want. You'll have "knobs" for increasing or decreasing compile-time checks for any property and invariant you desire. Or don't desire. You'll be able to switch between Lisp-style dynamic typing, Haskell-style static typing, and probably even C-style weak/no-typing. In the same program.

Statically-verified: Haskell is clearly moving towards dependent typing, which in theory, allows the expression of arbitrary invariants that are maintained statically, without having to run the program. Dependent typing is the weirdest and most awesome thing to expect of programming, this side of quantum computers.

Lisp, as it stands, can't do any of that, and won't be able to do any of that. That's simply a fact. Why? Because it's coming at the problem from the wrong direction. Trying to graft an interesting type system or verification onto Lisp is simply too heroic and ill-specified a task. Lisp starts with ultimate freedom/unsafety, and you can't constrain it. Haskell starts with ultimate bondage/safety, and then, piece by piece, adds that freedom back. On a theoretically well-founded basis.

Right now, Lisp has certain advantages. As a command or scripting language where ultimate dynamism is desired (Emacs), it's still clearly superior. But Haskell is encroaching on its habitat from all sides, just like it's encroaching on the habitats of all other languages. Right now it may appear pointy-headed and harmless. But I think it's unstoppable, and doomed to succeed.

How does that make Lisp a big hack? If my theory is right, then once Haskell will be able to do everything Lisp can do now (and more), all the while maintaining adjustable safety and static verification, I think it will be justified to call Lisp a big hack - because it lacks the possibility of this safety and verification, in principle. (Of course you have to subscribe to the idea that this safety and verification is something that's good and superior. I do.)

(HN, Reddit)

random notes

  • Great discussion over on LtU: The AST Typing Problem:

    So to summarize, my basic argument is that the separation into distinct IRs may not necessarily reduce the overall complexity of your compiler, but it will certainly modularize it and make it easier to evolve.

    As for the compilation speed target, coupled with a desire to use the static type system of the implementation language to enforce a higher dimension of correctness than seems to be borne out in existing compilers, and your overall goal of creating a new language, be careful not to ask for too many miracles :-) -- Ben L. Titzer

  • Emacs 24 finally gets the much-needed create-animated-image functionality.
  • Good example of collaborative software design on LKML: [concept & "good taste" review] persistent store.

More fully-featured, modern Lisps, pulleezz


``Horsey Horseless''

While I don't like the tone of my earlier post, No more "minimal early Lisps", pulleezz, I stand by its core message: implementing a "minimal early Lisp" may actually be bad for you.

Why? An analogy: if you want to learn how to build a car, learning to build a horse carriage doesn't seem right. And the difference between "LISP" and modern Lisps is similar to that between horse carriages and cars.

IMO, modern "mainstream" Lisp (Common Lisp, Scheme, Dylan, Goo, EuLisp, ISLISP) is the king of dynamic languages. All the others (PHP, Lua, Python, Ruby, ...) are merely variations on the theme.

Compared to Haskell, Lisp is a big hack. But it has its uses. And over the course of decades, Lispers have accumulated a wealth of tricks to cope with the hackish character inherent in Lisp.

So if you reimplement an early Lisp, you're missing out on a lot of stuff, just like Lisp's "successors" in the dynamic language area do. It's a shame, really. IMO, if you care about Lisp, you owe it to yourself to not only learn about "LISP", but also what happened in the 5 decades since.

And I also think you owe it to others. The world is already plagued enough by bad programming languages. If you want to put out a new one, even if it's meant just as a toy, by all means do us all a favor and study history first.

Here's a list of some of the stuff that I think every Lisp (re-)implementer should care about:

Bindings


Understand the difference between SETQ and DEFPARAMETER for global variables.

Understand PROGV and BOUNDP. Ponder their absence in Scheme.

Understand Scheme's LET, LET*, LETREC, and LETREC*. (Yeah, Schemers are funny people. But sometimes they do have a point.)

Understand the difference between Common Lisp's dynamic variables and Scheme's parameter objects (SRFI 39). Compare to EuLisp's DYNAMIC-LET.

Understand why Clozure CL offers DEFSTATIC.

Macros

Understand EVAL-WHEN. Understand the need for something like EVAL-WHEN.

Understand Scheme's expansion process.

Understand hygienic macros, preferably SRFI 72.

Understand why some Schemes use full phase separation, while others like to collapse meta-levels (also).

Understand negative meta-levels. (Once you do, please tell me how they work.)

Control Flow

Understand CATCH/THROW, TAGBODY/GO, and BLOCK/RETURN-FROM, and how they can be implemented in terms of each other.

Understand UNWIND-PROTECT vs DYNAMIC-WIND.

Understand the condition firewall.

Contrast Dylan's, Goo's, and CL's condition systems.

Conclusion

These are just some of the ingredients shared by "mainstream" modern Lisps. They have been developed and refined over the course of 5 decades by some of our best minds. You cannot understand modern Lisp, its history, or contribute to its glorious future without knowing about them.

If you want to learn how to build horse carriages, implement a "LISP". If you want to learn how to build cars, study the items in this list.

</soapbox>

(Update: See the HN discussion for some clarifications.)