Ever since the Scheme R6RS roadblock, I've been looking to branch out into other Lisps outside the Scheme ecosystem. The good news is that there's no shortage of nascent Lisps and Lisp-wannabes, with many interesting new undertakings being launched month upon month.
There's one in particular, called Clojure (pronounced closure), that was unveiled recently and effortlessly stands out from the bunch. There's much to like about Clojure, but first and foremost, it follows a principle I'm very fond of: code talks, bullshit walks.
That is to say, it's not difficult to imagine any number of interesting directions to take Lisp into, nor is it generally all that much effort to implement proof-of-concepts of such ideas (it's Lisp, after all); but to actually create a full integrated, practical and performant system, that's another matter entirely.
The difference between toy and tool is more than quantitative, and the leap from an SICP metacircular interpreter to something deserving of the hallowed title "Lisp system" is non-trivial, requiring not just deep mastery of the fundamentals but also the discipline and opportunity to take care of all the "boring" stuff so as to ship it. As I know all too well from plenty of practice, this last so-called 20% is where most efforts ultimately wither out to oblivion.
Thus it's a pleasant surprise to discover that while Clojure has no shortage of goodies, it's also remarkably mature and usable right here and now. The author, Rich Hickey, says that he has worked on it for two years, and I think it's admirable that he kept tight-lipped about it for the duration; as a first alpha release, Clojure as it is currently available simply rocks. No Marimba phenomenon, here.
As for the goodies, how does a concurrent, functional Lisp1 with software transactional memory sound like? To me, it sounds like the future. (And that's just the tagline, there's much more.)
I've thought for a while now that if you took a purely functional subset of Scheme (pre-R6RS), sprinkled it with the best of Haskell (without the whole mandatory bondage and discipline, thanks) and built it all on top of an Erlang-like runtime, that might form a pretty compelling basis for a serious attempt at a new practical Lisp dialect. Clojure is perhaps not quite the particular mix that I had in mind, but it's very much in the ballpark.
Indeed Clojure embodies the kind of Lisp I'm interested in, that is, a pragmatic go-getter system actively drawing on the best new computer science research and pushing Lisp ever further out on the frontier of the future, instead of complacently sitting around arguing about how many lambdas can dance on a parenthesis while wondering why the competition is drawing nearer.
Considering how new Clojure is, there is a commendable amount of documentation already available, and the user community is growing in leaps and bounds, drawing both Common Lispers and Schemers as well as a good number of people entirely new to Lisp. The momentum that Clojure has going is tangible.
On the flip side, and these are really pretty serious omissions but may perhaps be intractable on the JVM at present, Clojure lacks tail recursion optimization and continuations. Also, while the concurrency features are impressive, I'm as of yet unsure how well-suited Clojure's JVM-based threading might be to a massively parallel programming style such as what I've been using with the lightweight processes in Gambit and Erlang, both of which will run millions processes without flinching. The author explains that these use cases are kind of supported but perhaps not really Clojure's sweet spot.
Now, given these JVM-imposed constraints, I should admit that I put off looking into Clojure for several months just given that it's based on the JVM; not only have I not recently had any reason to use the JVM, but my antipathy to Java is long-standing and well-documented.
However, in his LispNYC talk, Rich Hickey makes compelling points about why he based Clojure on the JVM, and I can't help but agree with many of them. Similarly, Neal Ford in his superb recent JRuby-related interview on JavaWorld makes a case that the actual positive legacy of Java will be the JVM as an established and widespread high-performance platform and infrastructure for enabling polyglot programming with the already more than 200 languages (including JRuby, Rhino, Jaskell and Jython) that you can run on it. I can buy that argument, too.
So, I'm coming to the unwelcome conclusion that my distaste for Java the language may have blinded me to the potential of the JVM as a platform for hosting other more powerful programming languages. I haven't much followed the JVM's evolution for years now, but intend to keep a closer eye on it in the future - especially the ongoing work around JSR 292 that aims to make the JVM suck less for hosting dynamic languages.
In any case, I'll be taking Clojure out for a spin. It's definitely a singularly unique new Lisp well worth looking at and learning from. I highly recommend Rich Hickey's two-hour LispNYC presentation which I can only describe as delightfully competent. (Also, in case STM is an unfamiliar concept, Simon Peyton-Jones's brief introduction at OSCON last year should be able to fill in the gaps some.)
SOURCES := $(wildcard *.hs) $(wildcard *.lhs)To run the tests, first we collect the list of modified modules and save the names of their tests (Foo.tests, Bar.tests, etc.) in a temporary file .test/args.
TESTS := $(foreach src,$(SOURCES),.test/$(src))
.test/%hs: %hsNow every time we run the tests, we first make a fresh .test/args file, and then we run GHC with a command-line option to evaluate an expression that runs those tests:
@if grep '^tests ' $< > /dev/null 2>&1 ; then \
touch $@ ; \
echo $* | sed -e 's/\..*/.tests/' >> .test/args ; \
fi
test: teststart $(TESTS)It's also useful to have a target that always runs all the tests:
@echo ghc Test -e "\"Test.run [ `xargs -a .test/args | tr ' ' ','` ]\""
@ghc Test -e "Test.run [ `xargs -a .test/args | tr ' ' ','` ]"
teststart:
@mkdir -p .test
@$(RM) -f .test/args
@touch .test/args
retest: testclean testThis assumes the existence of a simple test harness module:
testclean:
$(RM) -rf .test
module Test (run) where
import Test.HUnit
run :: [Test] -> IO ()
run ts = (runTestTT $ TestList ts) >> (return ())
Weakliem’s First Rule of Application Development states, roughly, that design is less important than functionality. While I agree in principle I think his proof is lacking in a number of places. Specifically, he states: “Recall that when Google first appeared, most search engines embraced the design philosophy still in evidence at MSN.com: bright and noisy, yet roughly equivalent in functionality. Google was positively audacious in both its austerity and its function. ... Similarly, My employer’s website is frequently ridiculed for being amateurishly designed” What I think he forgets is all design, however amateurish, still conveys something. Google's (to my eyes incredibly ugly) logo said “hey, we're a bunch of geeks having some fun” which exactly matches the company culture and helps attract all those PhDs that Google employs. Similarly Gordon's employer's website looks like it was designed by someone's cousin, but that is the right look for its clients. It gives the website credibility with the consumers who put down a large chunk of change for a holiday they can only afford once a year. Good design is design that is right for the target audience, which can be something very different to aesthetically pleasing design.
join :: Eq a => [(a,b)] -> [(a,c)] -> [(a,(b,c))]
join a1 a2 = [ (a,(b,c)) | (a,b) - a1, c - [ c | (a',c) - a2, a == a' ] ]
infixr 5 ⋈
(⋈) :: Eq a => [(a,b)] -> [(a,c)] -> [(a,(b,c))]
(⋈) = join
-- for example:
[("a",1),("b",2),("c",3)] ⋈ [("a",'a'),("b",'b'),("c",'c')]
(define (foo x y)What if, at the REPL (i.e., in debug mode only), closures were openable? You could use dot-notation to refer to them, and all you'd have to do is somehow provide the extra bindings needed to fill in their environment. Keyword arguments, maybe?
(define (helper z)
... x ... y ...)
...)
> ((close foo.helper [#:x 42] [#:y 'blah]) "value of z")
'result-value-ftw!
infixr 2 ^^^
(^^^) :: Maybe a -> Maybe a -> Maybe a
Nothing ^^^ r@(Just _) = r
l@(Just _) ^^^ Nothing = l
_ ^^^ _ = Nothing
infixr 2 |||This gives you a concise notation for branching in the Maybe monad, so instead of having to write:
(|||) :: Maybe a -> Maybe a -> Maybe a
Nothing ||| x = x
x ||| _ = x
case x `lookup` env1 ofyou can write:
Just v -> Just $ f1 v
Nothing -> case x `lookup` env2 of
Just v -> Just $ f2 v
Nothing -> Nothing
do { v - x `lookup` env1; return $ f1 v } |||
do { v - x `lookup` env2; return $ f2 v }This gives you the same kind of idiom that Schemers use or for. Come to think of it, I guess you could also write the analog of and:infixr 3 &&&I haven't used this one yet, but it seems natural.
(&&&) :: Maybe a -> Maybe a -> Maybe a
Just _ &&& r@(Just _) = r
_ &&& _ = Nothing
(if-it 42 (+ it 1) #f) ; => 43The difficulty in getting such a macro right comes when you try to write another macro that expands into if-it. To quote the mzscheme manual's section on macros, "macros that expand into non-hygienic macros rarely work as intended."
(when-it 42 (+ it 1)) ; => 43On the other hand, we might want to use if-it to implement the hygienic or macro, which shouldn't capture any variables.
(let ([it 10]) (or #f it)) ; => 10First, here's the implementation of if-it: we create an identifier for it with the same lexical context as the operator of the expression:
(define-syntax (if-it stx)The references that will be captured by the introduced binding of it are the ones that were introduced into the program in the same expansion step as the occurrence of if-it in the macro call. In particular, if the occurrence of if-it was in the original program (i.e., written explicitly by the programmer), it captures references to it that were in the original program; if the occurrence of if-it is the result of a macro expansion, it captures only those references to it that were generated in that same expansion step.
(syntax-case stx ()
[(op e1 e2 e3)
(with-syntax ([it (datum->syntax #'op 'it)])
#'(let ([it e1])
(if it e2 e3)))]))
(define-syntax orSince the reference to it appears in the same expansion step as the occurrence of if-it, that reference is captured, but no references to it within subexpressions e1 or e2 (which had to have already been there before this expansion step) are captured.
(syntax-rules ()
[(op e1 e2)
(if-it e1 it e2)]))
(define-syntax (when-it stx)Here we once again create an identifier with the same lexical context as the operator, and we bind it to the occurrence of it introduced by if-it.
(syntax-case stx ()
[(op e1 e2)
(with-syntax ([it* (datum->syntax #'op 'it)])
#'(if-it e1 (let ([it* it]) e2) (void)))]))
If you do any macro programming in PLT Scheme you are sure to run into the dreaded “no #%app syntax transformer is bound” error message at some point. Though puzzling, the fix is actually quite simple in almost all cases. Assuming you're using 3.99, you either need to:
(require (for-syntax scheme/base))(require (for-template scheme/base))What the error means is that some syntax has expanded in a function application, but #%app, the PLT Scheme primitive that actually handles application, is not bound in the phase in which the syntax is being evaluated. Requiring for-syntax will bind #%app in the phase before the current evaluation phase, while requiring for-template will bind #%app in the phase after. In most cases you want for-syntax. However, if you are writing functions that return syntax that is then inserted into a program (such a function would be required for-syntax elsewhere) you must use the other form, to make sure the syntax has #%app available to it.
For Valentine's Day we have created a new website, Smut Shorts. If you have something to say about love or lust, and can do so in 500 characters or less, then please add it to the growing number of “shorts”. It's anonymous and fun. Just, no porn thanks.
If you're reading this site, you're probably interested in the technical details behind Smut Shorts. It is a collaboration between a number of people, most of whom have chosen to be anonymous. The majority of the coding was done by yours truly, and therefore in Scheme. It is running on the PLT Scheme webserver (version 3.99) and uses PostgreSQL as the back-end. I coded it up in about two days. It was a side-project, so it was a bit of rush job and there is lots still to do. If you break the site let me know and I'll try to fix it.
A few interesting lessons were learned from doing this site. It all comes down to scalability, which is something that has recently been on my mind a lot. In this case we want to scale down to the low end — the guy who is just hacking up something in his spare time and wants to get it done in a hurry. Our frameworks, Snooze and Lylux, are pretty good but they don't support a fast start. You have to create a whole bunch of files before you've even got your first page up. Furthermore, we've always avoided creating a templating mechanism, as we've said that we'd rather use smart people who can balance parens than create this unnecessary divide between designers and programmers. I now recognise this is a mistake. Had we a templating mechanism I could have pushed more design work to my collaborators. It's not that they're stupid (far far from it) but they're busy and don't have time to learn even Scheme basics just so they change a few lines of text. If we're gonna grow the Scheme web-hacking community it has to start with dudes messing around in their spare time, so we need to address the low-end of scale. The high end of scale can wait till the IPO ;-)
Ever thought about buying a franchise? Like the idea of running your own business but don't want the risk of trying it all alone? How about farming? Attracted by the notion of growing all your own food, and connecting with nature? How about both — franchised farming? Sounds unlikely, but that's essentially what SPIN farming is.
The core of SPIN farming is, well, farming, but not on the scale that most people associate with modern farming techniques. SPIN farmers typically work plots less than an acre in area, and achieve good returns by concentrating on the most profitable crops, and utilising crop rotation to increase yield. The best description I've found is here, though it isn't very detailed.
In itself the farming techniques aren't that radical. I remember learning in high school that crop rotation was one of the key innovations of the agrarian revoluation, and that was some time ago (both high school, and the agrarian revolution). What is novel is the way the SPIN farming business is run, and that's what causes me to call it a franchise. Contained in the guide books (the complete set can all be purchased online) is everything you'd expect from a good franchise: a business plan, marketing advice, and a detailed day-to-day workflow. In standardising the product and creating a reproducible process it really isn't any different from McDonalds. Now SPIN farming isn't a true franchise — you don't buy the right to use the name, and there isn't any ongoing fee. And there's no equivalent to Hamburger University either. At least not yet. It is still an interesting business model and one that I think has great potential, though perhaps not for the financial gain of the founders.
The franchise idea, believe it or not, has great relevance to computing. What is convention over configuration if not the computing equivalent of the franchise's reproducable process? Perhaps by regarding frameworks as franchises we can shift the emphasis from technical development to supporting the developer in every way they need to succeed. This seems to me a better goal for both framework developers and users.