Learning Clojure

5 views
Skip to first unread message

Brian W

unread,
Dec 10, 2008, 1:27:23 PM12/10/08
to Clojure
I've created a new Clojure intro at http://en.wikibooks.org/wiki/Learning_Clojure.
While Rich's screencasts and reference docs are great, they don't
always lay things out in a digestible order. My intro is meant as a
sequential tour through the essential concepts, not a practical
tutorial. In particular, my examples are deliberately cursory and
abstract, and my API coverage is very minimal.

I'm sure the text has some rough spots because at times I went ahead
and speculated about points I'm not 100% about. For instance, the way
I described the evaluation rules coherently explains how ((foo) bar)
is evaluated, but I'm not sure the rules I described are how Clojure
actually works. The text is also a bit out of date already, e.g. I
don't mention AOT or Atoms. So please read through the text and
correct or excise any gaffes, or please comment on deficiencies here
or the book's talk page.

One thing I'd like to add is a discussion about how to imitate
traditional single- or multiple-inheritance using multimethods,
including basics like how to organize such code into files and
namespaces, but I'm not clear on how this should be done myself.

Another issue I had is we don't have a good blanket term for Vars,
Refs, Agents, and Atoms. Rich sometimes calls them "reference types",
but that term already has a different meaning in Java. I considered
"meta-references", but then I realized that, say, the symbol pointing
to a Var is a meta-reference---a reference pointing to a reference---
not the Var itself. I've settled for now on "reference object" and
"reference object type", but these are confusingly close to "reference
type".

J. McConnell

unread,
Dec 10, 2008, 3:03:02 PM12/10/08
to clo...@googlegroups.com
On Wed, Dec 10, 2008 at 1:27 PM, Brian W <brian.th...@gmail.com> wrote:
>
> Another issue I had is we don't have a good blanket term for Vars,
> Refs, Agents, and Atoms. Rich sometimes calls them "reference types",
> but that term already has a different meaning in Java. I considered
> "meta-references", but then I realized that, say, the symbol pointing
> to a Var is a meta-reference---a reference pointing to a reference---
> not the Var itself. I've settled for now on "reference object" and
> "reference object type", but these are confusingly close to "reference
> type".

I would say to stick with "reference types" and just explain what is
meant by the term. I have a feeling that introducing new, undefined
terms will just confuse things more. In fact, it seems to me that both
Java and Clojure have a pretty similar definition for the term. More
or less, a reference type is a type whose value is a reference to a
data structure on the heap. I think that by taking a sentence or two
to point out the similarities and differences between the ways Java
and Clojure use the term, any confusion will be greatly reduced.

Regards,

- J.

Randall R Schulz

unread,
Dec 10, 2008, 3:10:29 PM12/10/08
to clo...@googlegroups.com
On Wednesday 10 December 2008 10:27, Brian W wrote:
> I've created a new Clojure intro at
> http://en.wikibooks.org/wiki/Learning_Clojure. ...
>
> ...

>
> Another issue I had is we don't have a good blanket term for Vars,
> Refs, Agents, and Atoms.

Speaking of these, your article mentions and describes only Var, Ref and
Agent in your "Reference Types" section.


> ...


Randall Schulz

Cosmin Stejerean

unread,
Dec 10, 2008, 4:09:55 PM12/10/08
to clo...@googlegroups.com
"The text is also a bit out of date already, e.g. I
don't mention AOT or Atoms."

Sounds like a known bug. 

--
Cosmin Stejerean
http://www.offbytwo.com

Brian Will

unread,
Dec 10, 2008, 4:32:19 PM12/10/08
to Clojure
A Java reference type is basically any type allocated on the heap. The
four Clojure reference types are particular Java reference types. My
complaint is this is exactly the sort of weirdness that causes
learners to scratch their heads. Not the biggest issue, sure, but this
sort of thing is nice to avoid when possible.

While we're at it, aren't "Var" and "Ref" backwards? Currently, Refs
are the things that get mutated in the normal course of a program
while a Var is just a top-level holder which is rarely changed.
Shouldn't the thing which is variable be the "Var" and the
(relatively) fixed thing be the "Ref"? The root problem here, though,
either way, is that "var" vs. "ref" makes a dubious semantic
distinction between already overloaded general-use terms. Sadly, you
see this a lot in naming because good naming is very often /freaking
hard/.

Again, not a big deal by itself, but if you never fix these mistakes,
they just build and build until a whole bunch of names in your system
become impenetrably meaningless to newcomers, like car and cdr, making
the whole system needlessly hard to learn.

I understand, though, that I'm uniquely obsessed with ease of
learning. One thing I find very exciting about Clojure--and something
which very few have noted--is that Clojure is a considerably easier
Lisp to learn: easier than CL, certainly, but also even easier than
Scheme. Hence my introduction: I wanted to distill that simple essence
into something sequentially readable and easily digestible. While the
language is still open for amendment, I hope Clojure takes some
measures to preserve and optimize ease of learning. Ease of learning
is a big part of what will give Clojure a chance that other Lisps
never had to see wide adoption.

On Dec 10, 12:03 pm, "J. McConnell" <jdo...@gmail.com> wrote:

J. McConnell

unread,
Dec 10, 2008, 5:17:17 PM12/10/08
to clo...@googlegroups.com
On Wed, Dec 10, 2008 at 4:32 PM, Brian Will <brian.th...@gmail.com> wrote:
>
> A Java reference type is basically any type allocated on the heap. The
> four Clojure reference types are particular Java reference types. My
> complaint is this is exactly the sort of weirdness that causes
> learners to scratch their heads. Not the biggest issue, sure, but this
> sort of thing is nice to avoid when possible.

First off, let me say that I've started looking through what you've
written and I really like what I've read so far, so good job and
thanks!

WRT the "reference type" vs. "reference object" terms, yes, it's
unfortunate that the "reference type" term is overloaded. However, it
seems like it is overloaded for a good reason in this case. Var's and
Ref's are types in Clojure, they are not objects, so I'm not sure that
"reference object" is accurate and that is more confusing to me than
the overloading of the term. Especially when I would expect that
anyone coming to Clojure would be reading the official docs in
parallel with your intro. and the official docs refer to "reference
types", so not using the same terminology can confuse things.

Again, I really like where you are going with this and you have
explained a lot of stuff better than I could, so you may very well be
right that someone new to Clojure will be less likely to stumble over
"reference objects". I just wanted to point out that, at least to
some, the other term carries more meaning, even if it is overloaded.

Regards,

- J.

Rich Hickey

unread,
Dec 10, 2008, 5:19:31 PM12/10/08
to clo...@googlegroups.com
On Wed, Dec 10, 2008 at 4:32 PM, Brian Will <brian.th...@gmail.com> wrote:
>

I don't think they are misnamed. Naming is hard, but Clojure does
decent job of it, and has lots of considered names. The real problem
is that there are only so many good names/words, and, if they have
been used before, people familiar with the old usage will have some
adjustment to do. 'Type allocated on the heap' is a semantic for
reference that doesn't deserve to stand forever.

All of the reference types can vary, and all are references in the
sense that they refer to something else, their value. Vars have an
historic tie-in to CL's dynamic variables, and when thread-locally
bound, have the closest semantics to ordinary variables in other
languages, supporting unmitigated set! Ref is short for transactional
reference, a mouthful, and tref, which wasn't any more immediately
meaningful than plain ref, and has a tie to ML's ref. Atoms and Agents
hint pretty well at their semantics, which still must be explained.

As far as the ease of learning, well, sometimes the easiest thing to
do is not discuss something until later, i.e. why talk about
thread-local binding and vars to a beginner?

The bottom line is that there won't be a one-to-one mapping for a
beginner since they are not likely to be coming from a language that
requires all mutation to be done via controlled locations, never mind
one with multiple semantics for such locations. It's simply going to
be something new to learn, with it's own nomenclature.

Ideally beginners should avoid all the reference types until they get
comfortable with FP.

Rich

Brian Will

unread,
Dec 10, 2008, 9:03:38 PM12/10/08
to Clojure
Thanks for explaining the origin of "var" and "ref". An important
thing you should do for learners is to explain their origin of odd/
cryptic names because that makes them much easier to remember. For
example, no text on C I've ever read explains the meaning of standard
library function names, like printf and sprintf, but the name and
purpose of those functions are far easier to remember if someone
simply tells you 'f' stands for 'formatted' and 's' stands for
'string'.

I should move the bits on Ref, Agent, and Atom towards the end. Vars
have to be introduced before I can discuss namespaces, but the thread-
local binding aspect of Vars can be deferred to later.

btw, you'll see a few notes I left in the text in square brackets
where I wasn't sure on some point. If someone could address those
questions, I'd appreciate it.

--Brian Will

Timothy Pratley

unread,
Dec 10, 2008, 9:52:26 PM12/10/08
to Clojure
Hi Brian,

Looks really good.

> My intro is meant as a
> sequential tour through the essential concepts, not a practical
> tutorial. In particular, my examples are deliberately cursory and
> abstract, and my API coverage is very minimal.

I have some comments which may go beyond your desired scope, but I
don't think would bloat it:

1) "Learning Clojure" launches straight into describing Clojure. I
think you should at least have a link to a more basic tutorial or
dedicate a paragraph to the syntax so that your concepts can be more
fully understood.
- I propose you should have a link to http://en.wikibooks.org/wiki/Clojure_Programming/Getting_Started
as a start reference
- followed by a paragraph on basic forms (I couldn't find a simple one
to link to a description so I suggest one below)
"Clojure programs are written in forms. Forms enclosed in parenthesis
indicate function calls. eg: (+ 1 2 3) calls the '+' function with
arguments 1 2 3 and returns 6, the sum of its arguments. New functions
can be defined like so: (defn average [x y] (/ (+ x y) 2)) here x
and y are symbols representing the input arguments. '/' is called to
divide the sum of x and y by 2. Note that forms are always in prefix
notation, ie: the operator is called with subsequent arguments. Now
average can be invoked as (average 3 5) and will return 4. <link to
more fully detailed description of forms for later reference>"

2) The functional programming section describes well the differences,
but for someone unfamiliar with FP terminology it can sound like just
a bunch of words. I think some examples really help make the concepts
concrete
"functions without side-effects"
imperative: void moveplayer( p, x, y ) // updates a player object
with a new location
OO: class player { void move( x, y ) } // again, mutates an existing
object
FP: freshp = moveplayer( oldp, x, y ) // a completely new player
has been created, the old player is unaffected
In imperative you only know that p has changed because the function
name hints it. In FP oldp is preserved (you don't need to worry about
what happened to it - nothing can happen to it) and it is explicit
that freshp is a result of moving.

"immutable data"
Consider removing an item from a list:
imperative solution would modify the list in place.
FP solution would return a completely new list, leaving the original
in place.
This sounds on the surface to be wasteful, but there are many ways
that this is optimized by the compiler to be very efficient.
You can immediately see that the FP solution can be used in parallel,
while the imperative cannot.

"first-class functions"
(map + [1 2 3] [4 5 6]) returns (5 7 9). Using functions as arguments
to other functions is very powerful.

"function-based control flow"
You already provide an example with if, which is probably enough. You
might want to give an explicit example though:
(if true (+ 1 2)) returns 3


3) Functions section I would say should go before data types, seeing
it is so central a concept. You mention arity, again I think a short
example would help:
(defn myfun [a & rest] (+ a (apply + rest)))
(myfun 1 2 3 4) returns 10
; The ampersand indicates that the next symbol will take all remaining
arguments into a vector


I noticed in your latter sections you have more examples, which is
great.

I really like that you have a de-structuring section! This is one of
the really powerful features that seems to be completely undocumented
on the website and wiki (that is to say I can't find it, it may
exist). I think this section would be great on the official page also.





Regards,
Tim.

Stephen C. Gilardi

unread,
Dec 10, 2008, 9:56:38 PM12/10/08
to clo...@googlegroups.com

On Dec 10, 2008, at 9:03 PM, Brian Will wrote:

> btw, you'll see a few notes I left in the text in square brackets
> where I wasn't sure on some point. If someone could address those
> questions, I'd appreciate it.

> [hmm, what are the chances of a false positive due to hash
> collision? are the odds astronomical and therefore acceptable?

A correct use of hash codes in equality testing would be only to
enable returning false (indicating not equal) quickly. Hash codes
cannot be used to ensure that objects are equal. If two objects have
the same hash code, they need to be checked in more detail to
determine whether or not they are also equal.

For the Java take on this, please see:

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#hashCode()

In particular:

The general contract of hashCode is:

* Whenever it is invoked on the same object more than once during
an execution of a Java application, the hashCode method must
consistently return the same integer, provided no information used in
equals comparisons on the object is modified. This integer need not
remain consistent from one execution of an application to another
execution of the same application.
* If two objects are equal according to the equals(Object)
method, then calling the hashCode method on each of the two objects
must produce the same integer result.
* It is not required that if two objects are unequal according to
the equals(java.lang.Object) method, then calling the hashCode method
on each of the two objects must produce distinct integer results.
However, the programmer should be aware that producing distinct
integer results for unequal objects may improve the performance of
hashtables.

The chances of a false positive on an equality test due to hash
collision is zero.

> and what about Java strings; is it simply a call to equals?]

A non-trivial call to clojure.core/= reduces to a series of one or
more calls to clojure.core/= with two arguments. The two argument case
is handled by clojure.lang.Util.equals. After a quick return of "true"
if the objects are identical and some check whether this is a
comparison involving nil or numbers (which get a bit of special
treatment), the general case eventually calls the equals method of the
first object with the second as its argument. If the first object is a
string, that does boil down to a call to the string's equals method.

--Steve

Brian Will

unread,
Dec 10, 2008, 10:43:51 PM12/10/08
to Clojure
Tim:

Rich talks about destructuring in the part about "let" on the "special
forms" page.

The discussion of functions and basic syntax is deliberately delayed
because of dependencies, e.g. evaluation can't really be understood
without understanding the reader, and explaining the reader involves
talking about lists, vectors, maps, and other types. Besides, I think
a data-to-code approach is especially appropriate in Lisp.

The decision to use cursory, abstract examples is deliberate. When
you're trying to demonstrate how a language mechanism actually works,
it's distracting when the code example is meant to actually do
something because it makes the reader untangle the what from the how.
With foo-bar code, the reader can focus on what exactly is happening
in the code without reference to any goal.

The other reason the text is example poor is that I deliberately avoid
introducing syntax before semantics. Syntax is a lot of meaningless
ascii jumble until you're familiar with the concepts being expressed.

Of course, concrete examples should soon follow once the concepts are
laid out, but I'm terrible and coming up with such examples. If you
have any examples to add, please add them yourself (it is a wiki
page). Something I don't like about examples--particularly long,
practical examples--is that they fill up the text, segregating the
core information into little chunks that are hard to browse. So
instead of putting such examples in the main text, perhaps they can go
on their own page where the main text simply links to them. Readers
can decide for themselves if they need to see more context about a
topic.

Really, though, like with any expository writing, what are needed are
guinea pigs to actually read the damn thing. Hopefully keeping the
whole text reasonably short will encourage volunteers.

Thanks for your feedback.

--Brian Will

Timothy Pratley

unread,
Dec 11, 2008, 12:55:48 AM12/11/08
to Clojure
Hi Brian,


> Rich talks about destructuring in the part about "let" on the "special
> forms" page.

Ah indeed, thanks for pointing that out :)


> If you have any examples to add, please add them yourself (it is a wiki
> page).

You've given some really good reasons why I shouldn't mess with it
*chuckle* so I'm getting mixed messages. If you do want me to add
example link, I'm happy to do that but for now I'm assuming you'd
prefer it left as is :)


> Thanks for your feedback.

You are most welcome.


Regards,
Tim.

Alex Burka

unread,
Dec 11, 2008, 1:06:48 AM12/11/08
to clo...@googlegroups.com
To the debate on whether there should be examples early in the text,
here are my two cents:

When I click on something called "Learning [programming language]" I
like to see a representative example of the syntax early on. If
there's just text as far as the eye can see (that is, the first
screen) it is off-putting for some reason. After all, it is a
programming language.

Alex

janus

unread,
Dec 11, 2008, 8:58:50 AM12/11/08
to Clojure
Timothy,
Your post is a great one indeed , you have developed a template that
anyone could use to introduce Clojure. I would implore to fresh out
thoughts and deepen it for all to enjoy.

Emeka

samppi

unread,
Dec 11, 2008, 10:41:11 AM12/11/08
to Clojure
Great article, but I'm not sure this part in the keyword section is
correct:

"Keywords exist simply because, as you'll see, it's useful to have
names in code which are symbol-like but not actually symbols. Keywords
have no concept of being namespace qualified as they have nothing to
do with namespaces."

Isn't it possible to qualify keywords like :clojure/tag?

Randall R Schulz

unread,
Dec 11, 2008, 10:48:35 AM12/11/08
to clo...@googlegroups.com
On Thursday 11 December 2008 07:41, samppi wrote:
> Great article, but I'm not sure this part in the keyword section is
> correct:
>
> "Keywords exist simply because, as you'll see, it's useful to have
> names in code which are symbol-like but not actually symbols.
> Keywords have no concept of being namespace qualified as they have
> nothing to do with namespaces."
>
> Isn't it possible to qualify keywords like :clojure/tag?

It is.

Furthermore, keywords have the fascinating property of being functions
over maps, effecting look-up of their entry, if any, in the map to
which they're applied. Symmetrically, and more conventionally, maps are
functions of keywords.

Both cute _and_ clever. And functional.


Randall Schulz

Brian Will

unread,
Dec 11, 2008, 12:41:47 PM12/11/08
to Clojure
Tim, just go ahead and make any changes you like. If I don't like
them, I can always revert ;) Actually, I'm sure anything you add we
can find a place for, but like I said, that would likely be a separate
example page in most cases.

Thanks, Randall, I mention keywords-as-functions where I talk about
collection functions, but I forgot maps-as-functions.

Perhaps future discussion of the text should move to the talk page on
the wiki. This thread's getting pretty long.

rb

unread,
Dec 11, 2008, 2:35:13 PM12/11/08
to Clojure


On Dec 11, 7:06 am, Alex Burka <zapper3...@gmail.com> wrote:
> To the debate on whether there should be examples early in the text,  
> here are my two cents:
>
> When I click on something called "Learning [programming language]" I  
> like to see a representative example of the syntax early on. If  
> there's just text as far as the eye can see (that is, the first  
> screen) it is off-putting for some reason. After all, it is a  
> programming language.
>


Hi,

I've discovered Clojure recently, and so I guess I'm part of the
target audience for such a document :-)
I must say I agree with Alex: I would really like to see code
examples.

From the title, I had expected a step by step tutorial for building my
first Clojure program.
I find your documentation *very* interesting but I filed it under
'Understanding Clojure' ;-)


Raph
Reply all
Reply to author
Forward
0 new messages