LispCast Episode 7

February 28th, 2008

So, I was going to wait on this, but why not release it now.

Yeah!

I’ve got an embedded flash player from blip.tv. Don’t mind the crappy resolution in the thumbnail — it’s much better after you hit play. Try it fullscreen, too, for a better experience.You can also download the OGG here. Here’s the code.

There are two more videos in the series to go . . . The next one is about . . . dare I ruin the surprise . . . continuation-based web-development. After that, I package it all up (in the next episode) using asdf. Stay tuned.Oh, and the end of the series is not the end of LispCast. As I’ve already mentioned in the comments of another post, the blog format is over. I’m switching to a traditional CMS. But that doesn’t mean I won’t make more videos. I’ll just stick to videos, articles, and maybe some reference documents. That’s what I like to do, and nothing’s going to stop me. I’ll post all about the change and my reasoning soon. That will be my last blog-style post. I plan on having a small feed of updates (like posts saying “A new video is posted, check it out here.”) but probably not much more. But enough, go watch the video. These videos are a labor of love. I can’t know how good they are, how bad they are, or what more you want to see if you don’t let me know what you think.

Popularity: 5% [?]

The Arc Debate

February 28th, 2008

Hey, everyone, Arc is out! As if you didn’t know. I mean, I think the internet backbone has been saturated with refreshing news.ycombinator.com to be able to post the next reply.

But seriously, as one of my last blog posts, I thought I might shed a meager light on the issue. There are a lot of statements, many true, many untrue, many other, that have been made about Arc. I’d like to address some of them, one at a time.

First, though, if you don’t know, Arc is a recently-released Lisp programming language by Lisp pundit Paul Graham. He is also the guy who started the Y Combinator venture capital firm. And he wrote news.ycombinator.com in Arc before he released the language to the public. Now it’s out, and look at the raucous.

So, some of the finer points about the Arc debate:

People ask Why doesn’t it do Unicode? The language is meaningless if I can’t use Unicode.

See: here briefly here here here

This seems to be one that Paul Graham has tried to address himself. Simple answer: Paul Graham doesn’t need it, it doesn’t give him much value, and it’s not one of his priorities. What were you expecting? A Python killer? Everything that has ever been said about Arc by Paul Graham has indicated a small, minimal language based on a few principles: terseness of programs and axiomatic definition of the language and libraries.

People say It’s just a bunch of name changes from Common Lisp, with only minor extensions. Students write their own Scheme interpreter in a semester in college. This is nothing different.

Ok, yeah, you could write it in Lisp. And guess what, he did. But he wanted to do it in a different direction from where scheme and common lisp went. Above, he wanted to maintain the principles that he set out to abide by from the core of the language up. That’s why it’s different. It’s a fresh and evolving start. Yeah, you could write something similar in a semester in college. But the fact that it is stable (it runs news.ycombinator.com) and that the programs continue to get shorter shows that it’s more than that and that his principles are still working. It is an experiment in language design, not a fixed point in language practicality.

People say After six years, this is it?

Well, in those 6 years he has obviously been pretty busy with spending his millions. Give him a break.

People say Continuation-based web programming is equivalent to cookies/sessions/query string state.

No. They are not. Continuation-based web programming lets you program in a natural, structured programming style. You avoid the spaghetti code of html links you have to write. Continuation-based web programming lets you program in a functional style, not a goto style like the rest of them. Functional style programming is better for exploratory programming, which is one of the reasons Paul Graham started to write Arc. And one of the main reasons he gives for choosing Lisp in general. And it trivially fixes the back-button problem. Oh, why not a list of all the reasons to use continuations?

  • No back-button problems
  • Multiple tabs/windows with different state
  • Functional style (forms look like function calls)
  • Easy to modify/maintain
  • Easy to read
  • Easy to write
  • Easy to wrap with more functions/macros

People say Continuation-based web programming lets you do all sorts of cool stuff, but what about global state which is shared by all windows/tabs?

Yeah, you know what? Multiple continuations can be closed over the same variables. That means you don’t have to worry about that. Or, you can just use sessions, if you really want.

People say Arc uses tables-based layout. What about CSS and accessibility?

Yeah, this one is tough. Paul Graham says he uses tables because they are more programmer-friendly (they let you explore). But this leaves screen-readers (for the seeing-impaired) in a mire of rows and columns, with no discernible order. It just isn’t important enough to him.

Well, this post is pretty messy, disorganized, and whatever. I don’t care. Here are a bunch of links, if you want to read all of the buzz. You might also like them if you’re a Google linkbot.

Lisp at Light Speed: Arc debarks
Hacker News | Take the Arc Challenge
Take the Arc Challenge
Arc - the Hundred Second Language - Robert Synnott
Arc is not a lot, but could it be?
Rondam Ramblings: My take on Arc
[FoRK] Re: Arc’s out, Nu vs. newLISP, and a retort to Paul Graham’s elitism
Stevey’s Blog Rants: Lisp is Not an Acceptable Lisp
Stevey’s Blog Rants: Lisp is Not an Acceptable Lisp
The Software Maven: An Arc-Tangent
Lisp at Light Speed: Arc debarks
Arc Forum | Infix Math
programming: Dear Proggit: Lets not start posting every toy lisp interpreter every hacker has ever made with titles like “This guy made his own arc in 2 weeks!”. Thanks.
Paul Graham’s Arc is released today… what is the long term impact? - comp.lang.lisp | Google Groups
Arc - An Unappreciated Approach to Language Design - Beautiful Code
Arc is released | Lambda the Ultimate
On Code: The ‘pre Arc’ Arc
Arc’s Out
Lisp at Light Speed: Arc Brevity
Zach’s Journal - Like a Bi-Metallic Strip
Drinkable Chicken » Arc, first impressions
(cadr life): Lazy Lists in Arc
smuglispweeny: Arc!Cells: It’s Alive!!!
smuglispweeny: Arc!Cells: Baby Steps
Arc Forum | Musings on Language Design
John Graham-Cumming: The Arc Challenge Explained
Fixing Software: A Wiki in Arc
Arc Forum | I have more challenge for you.Do the same thing, but make it survive a reboot of…
Object Oriented Arc
Arc’s if « Occasionally sane
ArcLite - Arc Ported to JavaScript
Lukas Renggli: We take the Arc Challenge
Lukas Renggli: We take the Arc Challenge
smuglispweeny: Meaningless First Impressions of Arc
Listening To Reason: Some potential advantages of Arc
Dude where’s my charset! | Schmevelopment
search results
On Arc, Paul Graham, and Unicode support as an exercise for the programmer // plasmasturm.org
Arc’s Out
ndanger.organism ::
blog :: Language design and Paul Graham’s dirty strings

Paul Graham on trolls | MetaFilter

Popularity: 7% [?]

Applying design principles to software, Part III

February 13th, 2008

Please teach me design.

Software design can be beautiful. It can bring joy to your work. Design is about usability. A beautiful object inspires good use. Good design teaches the user to use the object properly. A well designed library will bring joy to its users. It can instil a passion to learn more and improve skills. I love good design.

This is the third article in a series in which I explore how to apply principles of design to writing software libraries. The ideas in these articles were inspired while reading The Design of Everyday Things, a wonderful book by Donald Norman. There is one principal insight I am trying to explore: that the principles of design presented in the book can help us make software libraries that are intuitive to use.

In the last two articles, I explored the ideas of the Conceptual Model and Feedback. The Conceptual Model of the user is his/her understanding of the system. It’s what clues the user into how the system works. He/she builds the model from the System Image and through active experimentation, fueled by feedback. Feedback is when the library gives the user information about the effect of his/her actions.

In this article, I want to discuss the principle of constraints. Constraints limit actions. They can keep the user from breaking the system. Constraints can also guide the user to learn how to do what he/she needs to do. Constraints serve two purposes: preventing errors and helping the user figure out what actions he/she wants to take. Well designed constraints can vastly improve the usability of a library.

Constraints

Half of the ease of use of an object is in the object itself preventing errors, or at least making them undoable. If the object didn’t constrain them, the user would have to constrain him/herself from taking inappropriate actions.

Constraints are a good thing?

Yes — constraints are a good thing. Well conceived constraints can give the user the freedom to explore. Preventing horrible errors makes it safe for learning by experimentation. It also gives the user confidence that he/she doesn’t have to worry much about unexpected errors happening. Constraints free the user from having to keep track of every possible thing that could go wrong. The system sets up boundaries within which the user can play and learn.

The other half of the ease of use is in helping the user decide how to do what they want to do. It is easier to choose between a small list of options than a large list. By using constraints, you can help whittle down the list of possible actions to just a few — freeing the mind of the user to think of other things. Constraints help the user figure out how to do what he/she wants.

In the design of physical objects, there are three kinds of constraints:

1. Physical constraints

Physical constraints use the physical properties of the object to limit the possible actions. These constraints are the strongest constraints. They absolutely limit the actions, and they are out of the control of the user.

Example:
Square pegs won’t fit in round holes.

Physical constraints might not literally exist in software, but there is an analogue in software. Compiler constraints limit actions before runtime. Lisp checks the number of arguments at compile time. Some languages check the types of the arguments at compile time. These constraints absolutely limit the possible actions the user can take.

Another type of physical constraint is runtime constraints — such as dynamic type checking — that limit actions, though you won’t know until you run your program. Runtime assertions can limit the actions allowed based on the values or types of the arguments, or based on the state of the system.

We’ll keep metaphorically calling these constraints “physical”. Physical constraints should be used in any case where an action should absolutely be avoided.

Mental Constraints are next

2. Cultural constraints

Cultural constraints use the cultural meaning attributed to objects to limit the possible actions. Cultural constraints are not as strong as physical constraints, but they are still important. While they don’t stop the user from performing an action, they do guide the user in deciding whether it is appropriate.

Example:
Green means go, red means stop. Coloring buttons this way makes it clear which to push when.

It’s possible to use the meanings embedded in function names to communicate more than just what the function does. It can also communicate when to use it, how to use it, and any context that surrounds it. For instance, the ABORT method on a transaction object implies that no more operations can be performed on it. The word “abort” connotes death, an absolute end. It would be difficult to imagine an operation that an aborted transaction could perform that wasn’t RESTART, RESURRECT, etc. Cultural constraints help the user filter out impossible or inappropriate actions.

Naming functions is so important

Cultural constraints can be used instead of physical constraints or in addition to them. The cultural constraint helps the user figure out in which cases the action should be used. Well-named functions can make it intuitive how to use the library.

3. Logical constraints

Logical constraints limit the actions to what makes sense. Logical constraints are about as strong as cultural constraints. They don’t stop the user, but they can guide the user — especially when the user doesn’t know what to do next.

Example:
A logical constraint that we’re all familiar with is the process of elimination. If there’s only one choice left, it must be the right one.

The thing about programming is that it already relies on logic. Logical constraints form the basis of the whole enterprise. So it’s your job to make those constraints evident and clear. If something doesn’t make perfect sense, explain why — in the doc string or the error messages. The more your system makes sense to the user, the more natural it will feel.

The user is still going to make mistakes.

Yes, you’re right. So we’ll need to deal with that.

Preventing and Fixing Errors

Obviously, limiting the number of errors the user makes will make the system feel easier to use.

Let’s talk about two kinds of errors: program errors and usage errors.

Program errors are problems in the program: syntax errors, type errors, wrong number of arguments, etc. Some of these are checked by the compiler, some are checked at runtime. Program errors are usually absolutely clear and can be caught. When a program error occurs, it means the action the user tried to perform did not occur.

Usage errors are when you accidentally do the wrong thing. You delete the wrong file or you store the answer in the wrong variable. Something wrong happens in usage errors, whereas usually nothing happens with program errors. Usage errors can be destructive — something happened, but not what the user wanted.

Program errors are easier to constrain. Syntax is checked by the compiler. Many type errors are checked at runtime, etc. One of your jobs, as a library designer, is to set up those constraints. These can usually be done with a physical constraint to absolutely prevent the action.

Usage errors are harder to constrain. They happen when the user does something he/she could do, but by his/her own reconning shouldn’t do. Imagine the usage error of setting your clock an hour too slow. It’s only a usage error because you don’t want to be late for work. If you were moving to a different time zone, on the other hand, it wouldn’t be an error. The clock can’t know what the user is up to, so it shouldn’t constrain. But that leaves room for errors. If these can be prevented, they are prevented through use of logical and cultural constraints. However, sometimes they are unpreventable. In these cases, the only recourse is to allow the action to be undone.

But let’s look first a how we can limit the damage caused by usage errors as much as possible. One way to limit usage errors is to constrain them with cultural constraints or logical constraints.

Let’s look again at the cultural constraint we talked about before. The ABORT method implies that no more operations can be performed on the transaction object. And let’s assume that it’s still possible to call methods on the transaction even after the ABORT. As a cultural constraint, it does its job pretty well of preventing an error.

Another way to reduce the number of usage errors is to turn them into program errors. Then the system can deal with it by setting up a physical constraint.

Even though the ABORT method is well-named, nothing would stop the user from accidentally calling a subsequent method, causing an error. But if we instead throw an exception when a subsequent method is called, we prevent an error from happening: we’ve created a physical constraint. The cultural constraint is still there — it still guides the user to choose correct actions — but there’s a safety net now.

You can do this to a lot of your functions — throw exceptions as soon as you know they will result in errors. Those exceptions can inform the client code (with appropriate restarts) and the programmer (with informative error messages). We’ll talk about that later.

Finally, errors will happen anyway, no matter how easy it is to use. What you can do in this case is to make them visible and correctable. Provide query functions, as in the last article, to discover the result of the actions. Also provide functions that are complementary to each other. One does, the other undoes, and vice versa. And make it clear that they can be used that way.

For instance, the macros PUSH and POP are complementary. If you POP something, and then decide you shouldn’t have, you can then PUSH it back on, and the state has returned to where it was. No destruction — the error is fixed.

Learning from errors

Users want to learn from their mistakes.

Program errors are inevitable — no one writes perfect code. But fortunately, they can be learning experiences. The library can teach the user how to fix the error.In the last article we talked about feedback. There’s an important kind of feedback that I didn’t describe in much depth — exception messages. In addition to constraining behavior, exceptions also communicate to the user through their type and through the message they contain. This makes exceptions part of the System Image, and therefore a good way to teach the user how to use the system — how to fix the error and how to prevent it later.

If the error is a result of the user’s misunderstanding, you can explain how the user can fix his/her conceptual model in the message. Take, for example, if the user enters this at the REPL:

CL-USER> (+ "Hello" 8 )

It would be easy to tell the user “Argument of wrong type”.

With that message, the user knows that one of the arguments is of the wrong type, but not which one. We can do better — and the message is usually not this bad.

When I type the above code at my REPL, SBCL throws a TYPE-ERROR condition, with the message The value "Hello" is not of type NUMBER.

Well, putting the type of the condition together with the message, the user can deduce that the function wants a NUMBER, and “Hello” is not one of them. But notice that it does require a bit of a leap. Imagine if you were new to Lisp and didn’t think to look at the type of the condition, only at the message. What would you think? I know I would think “Of course “Hello” is not of type NUMBER, it’s a string. Tell me something I don’t know.” It told me something obvious — it’s nearly a truism that “Hello” is not a number. I have to put the pieces together to figure out why it’s telling me this.

We can do even better than SBCL’s default message. Yes, that message does communicate a lot, but there is a lot more that could be communicated, and in a clearer fashion. What if the function gave this message:
"+ represents the mathematical operation of addition, and therefore only accepts arguments of type NUMBER. The value "Hello" is of type STRING. Perhaps you meant to concatenate two strings. In that case, read the documentation for CONCATENATE by entering (documentation 'concatenate 'function) at the REPL. Perhaps you wanted to add the length of the string to a number. The function LENGTH returns the length of a string."

What does this give me? While it’s still not perfect, I would much rather see this error than the previous one. It tries to teach me when to use + and explain why a STRING value is inappropriate. It also suggests another function that I might have meant instead. It is broadening my understanding of the system. Errors are a great opportunity to teach the user.

Conclusion

Remember, you want to set up boundaries within which the user can play and explore. So what can you do to do that?

Ask yourself these questions:

Does the library limit wrong actions, either through compile-time or runtime checks?

Check the arguments and throw exceptions with helpful messages. Check the type and value of arguments to functions at runtime, as soon as it is clear that they will cause an error.

Does the library let you undo mistakes?

Provide query functions for the state, as described in the feedback article. Also, for every function, provide another function that is the complementary action like PUSH and POP so that when the user determines that the action was an error, he/she can undo it.

Do the names of my functions and macros and their arguments imply how and when they should be used?

Think about the meanings of the names of your functions. The subtle meanings can communicate a lot to the user.

A man’s pledge to a woman

If you’ve been following the articles, you’ve learned some powerful concepts: Conceptual Models, Feedback, and Constraints. You’ve also learned about keeping knowledge out of the head of the user, and developing a good system image. These concepts can help you define the experience of the users of your library. That concludes the design principles article. Perhaps in the future I will expound more on the process of design. Thanks for reading. I hope to hear all about how you’ve used these principles in your own software.

Popularity: 5% [?]