On a bit of a related subject one thing I've always idly wondered about Lisp and typography is how hard the curvy parens, together with not much indentation make it hard to line stuff up vertically.
There are a couple of things I see which influence the typographic decisions regarding the code. First is its size - not really big enough to justify a data abstraction for the strings forming regex's. Being standalone, there is a justification for inlining these that would not be there for a similar snippit from part of a larger system.
A second feature appears to be optimizing the layout to display in "forty lines." There are places where the lines could be shorter but aren't. Not abstracting the strings falls somewhat into this category.
Finally, the code snippet does not appear to be the output of a pretty printer. Instead the typography is based on considerations beyond readability.
I think it's an easy thing to get used to, especially if the parentheses are a bit thicker. They're just a more slender character than a curly brace, so they don't appear to take up as much space as the others even in a monospaced font.
Yeah, I agree. Lisp is best with small functional blocks.
"It used to be thought that you could judge someone's character by looking at the shape of his head. Whether or not this is true of people, it is generally true of Lisp programs. Functional programs have a different shape from imperative ones. The structure in a functional program comes entirely from the composition of arguments within expressions, and since arguments are indented, functional code will show more variation in indentation. Functional code looks fluid on the page; imperative code looks solid and blockish, like Basic." -- On Lisp (http://www.paulgraham.com/onlisptext.html; page 30)
As function size grows, it doesn't look quite so pretty. An example from news.arc:
(if no.user
(submit-login-warning url title showtext text)
(~and (or blank.url valid-url.url)
~blank.title)
(submit-page user url title showtext text retry*)
(len> title title-limit*)
(submit-page user url title showtext text toolong*)
(and blank.url blank.text)
(let dummy 34
(submit-page user url title showtext text bothblank*))
(let site sitename.url
(or big-spamsites*.site recent-spam.site))
(msgpage user spammage*)
(oversubmitting user ip 'story url)
(msgpage user toofast*)
(let s (create-story url process-title.title text user ip)
(story-ban-test user s ip url)
(when ignored.user (kill s 'ignored))
(submit-item user s)
(maybe-ban-ip s)
"newest"))
There's a multi-branch if here, with lots of else ifs. But it's hard to see. In my toy dialect[1], I've added[2] colons as syntactic sugar that expands to nothing:
(if no.user
: (submit-login-warning url title showtext text)
(~and (or blank.url valid-url.url)
~blank.title)
: (submit-page user url title showtext text retry*)
(len> title title-limit*)
: (submit-page user url title showtext text toolong*)
(and blank.url blank.text)
: (let dummy 34
(submit-page user url title showtext text bothblank*))
(let site sitename.url
(or big-spamsites*.site recent-spam.site))
: (msgpage user spammage*)
(oversubmitting user ip 'story url)
: (msgpage user toofast*)
: (let s (create-story url process-title.title text user ip)
(story-ban-test user s ip url)
(when ignored.user (kill s 'ignored))
(submit-item user s)
(maybe-ban-ip s)
"newest"))
(cond
[no.user
(submit-login-warning url title showtext text)]
[(~and (or blank.url valid-url.url)
~blank.title)
(submit-page user url title showtext text retry*)]
[(len> title title-limit*)
(submit-page user url title showtext text toolong*)]
[(and blank.url blank.text)
(let dummy 34
(submit-page user url title showtext text bothblank*))]
[(let site sitename.url
(or big-spamsites*.site recent-spam.site))
(msgpage user spammage*)]
[(oversubmitting user ip 'story url)
(msgpage user toofast*)]
[t
(let s (create-story url process-title.title text user ip)
(story-ban-test user s ip url)
(when ignored.user (kill s 'ignored))
(submit-item user s)
(maybe-ban-ip s)
"newest")])
Doesn't seem like an improvement. The big aha moment for me was to notice that in imperative languages (C, Java, python, ruby) you got the distinguishing separator between the check and the action:
if (test expr goes here) { action goes here; }
else if (next text expr) { ...; }
The intervening keywords also help, but the parens vs curlies are a huge visual signal.
A random elisp example:
See where the ends of the parenthesis point? Not straight up or down, but diagonally.Just a random thought... maybe it's just me.