Tuesday, February 15, 2011

Critique of Reason

A language will not succeed without a good name. I have recently invented a very good name and now I am looking for a suitable language.
                — Donald Knuth

Louis Reasoner is developing a new computer language. I'm letting him write this post.

“I have already done the most important part: the name of my language is Reason.

“The second most important thing is to come up with a trope. This will enable me to defend my language decisions with a pithy reply and call it a design philosophy. My trope is ‘reasonableness’. Reason will adhere to the philosophy of reasonableness. Anything in my language is, by definition, reasonable. Anything omitted is naturally unreasonable. Here is some more design philosophy:
  • Small is more reasonable than big.
  • Fast is more reasonable than slow.
  • Useful is more reasonable than useless.
  • Good is more reasonable than bad.
  • etc., etc., etc....
I'll generate more later.

“After the name and the self-styled philosophy, the next important thing is the syntax. The syntax of Reason is complicated, but reasonable. Bear with me. Although it might be a bit difficult to explain, it is, after sufficient practice, natural to use.

Reason borrows heavily from Lisp and Scheme. Most of the programming in Reason is done in ‘Lisp’ mode. Lisp mode is exactly what you think it would be: you write programs just like you would in Lisp, but with a few modifications to make things more reasonable.
  • Prefix notation for everything by default, with these exceptions:
    • Infix notation for arithmetic. — Let's just capitulate on this one. Operator precedence will be fleshed out later.
    • Excess parenthesis are ignored where it is unambiguous. You can pretty much always add parenthesis to make something clearer or to override operator precedence.
    • Function calls have the operator on the outside of the parenthesis. Not only is this more natural, it avoids weird interactions with operator precedence. Arguments are separated by commas.
    • Postfix notation for post-increment and post-decrement. — I just like these.
  • No ‘special’ forms. We get rid of
    • define — Lisp code tends to creep towards the right margin. Removing internal definitions makes the code ‘flatter’ and therefore easier to read. (Besides, it's hard to efficiently compile internal definitions.)
    • begin — No good reason to include it.
    • cond — Too many parenthesis.
    • let — Again, too many parenthesis.
    • lambda — This is just too advanced. Mere mortals can define a named procedure like anyone else would.
    • letrec — As if lambda wasn't confusing enough! Who comes up with this stuff?
    • quote — Too confusing. Actually, I wanted to leave quote in, but with infix and postfix in the mix I couldn't figure out where the quotation ended. I decided to punt.
  • Assignment is allowed. This is technically a ‘special form’, but it is so useful that I had to leave it in.
“Lisp mode is distinguished by its use of nesting as the primary means of combination. In fact, sequential combinations are disallowed. Lisp mode constructs are created compositionally from other Lisp mode constructs or primitives. Lisp mode is where most of the primitive procedures of the language are available.

“In order to radically simplify Lisp mode I had to introduce ‘Program’ mode. Program mode is more imperative than Lisp mode, so I tried to reduce the functionality and power of Program mode so that people would favor Lisp mode. Program mode has these features:
  • Special forms — Flow control, variable declaration and binding, conditionals, etc. are available in Program mode.
  • No artificially rigid syntax — Program mode is not really infix, prefix, or postfix. Each construct defines its own syntax. This improves readability for the built-in special forms. For example, the if construct can have a traditional else clause rather than just having two or three subconstructs like in Lisp.
  • No user-defined special forms — The user doesn't have to memorize any additional special forms beyond the built-in ones.
  • No arbitrary nesting — In Program mode, you can only nest a construct if the enclosing construct permits it. There is nesting, it is just controlled.
  • Concatenation for a means of combination — Lisp mode is obviously recursively evaluated. This isn't needed in Program mode because arbitrary nesting is disallowed. Instead, user constructs in Program mode are built by concatenation of Program mode primitives.
  • Easy switching to Lisp mode — You can always concatenate a Lisp mode construct with a Program mode construct. Originally, I didn't want to do this, but I ended up duplicating a lot of the Lisp mode functionality. By allowing the programmer to switch to Lisp mode pretty much whenever he wants, I avoided having to duplicate a lot of code.
“When you write a procedure in Reason, you start in Program mode. As I mentioned, you can switch to Lisp mode whenever you feel like it, but there is no way to switch back (this isn't strictly true, you can mimic the ability to switch back by use of a function call. The function body starts in Program mode.) You can define a procedure in either Program mode or Lisp mode. That is, you can explicitly mark a procedure as available in Program mode only or in Lisp mode only. Of course since you can always switch to Lisp mode, it makes no discernible difference if you define a procedure as Lisp mode only; you can use it in Program mode. But if you define a procedure in Program mode only, you simply cannot use it from Lisp mode. (It will be visible, you just can't call it.)

“I could go on for hours about how variables are looked up, but I don't want to bore everyone. I would like some feedback about my ideas so far, though.

How about it? Would any readers care to critique the design of Reason?