Previously, I've been starting my foray into Clojure-land with some pretty basic stuff, an intro to some datastructures. Now I'm going into more interesting territory: functions, namespaces, blocks & scope.
Functions make the Lisp go round
Functions Anonymous
Defining anonymous functions is done with this 'special form' ie. builtin primitive:
user> (fn [a] (+ a 1)) #<user$eval913$fn__914 user$eval913$fn__914@61ade7f6>
That's the internal name of an anonymous fun. Define and call it in one go:
user> ((fn [a] (+ a 1)) 3) 4
Or bind it to a var:
user> (def f (fn [a] (+ a 1))) #'user/f user> (f 4) 5
There's a macro to create named functions though, and you can also pass in a docstring:
user> (defn g "The g func" [a b] (* a b)) #'user/g user> (g 0 1) 0
Much more convenient of course.
Multiple Arieties, VarArgs
Like Erlang, Clojure supports functions with multiple arietes. I think I like Clojures' syntax better though:
user> (defn f ([a] (f a 2)) ([a b] (* a b))) #'user/f user> (f 2) 4 user> (f 2 3) 6
That feels pretty clean.
There's syntax to support varargs (optional arguments) as well. With a wrong number of args you'd get:
user> (f) ArityException Wrong number of args (0)...
For varargs, put an & after fixed args:
user> (defn h [a & moar] {a moar}) #'user/h
Optional arguments are passed in as a list:
user> (h 0 :a :b :c) {0 (:a :b :c)}
If they're ommitted, the list is nil:
user> (h 1) {1 nil}
We defined h to expect at least one argument though. When called without any arg, we still get a ArityException, as it should be:
user> (h) ArityException ...
The idiom to destructure varargs is similar to Erlangs', ie. individual positional parameters can be placed in a vector:
user> (defn h [& [one two]] (str one two)) #'user/h user> (h 1 2) "12" user> (h) ""
Reader Features and Inplace Functions
"Reader features" are basically Clojure terminology for a pre-processor with a set of predefined templates.
One of these is the in-place function. An in-place function is a shorthand for the anonymous function above, writing #(...) instead of (fn ...) and numbered args (where %1 is the first, %2 the seconded etc.). Defining a function to add 23 and calling it is:
user> (#( + %1 23) 2) 25
Locals, Loops, And Blocks
Blocks
Group several expressions with a do block. Useful in cases where syntactically only one expression is allowed, but more are needed for sideeffects:
user> (do (def a 2) (def b 5) (* a b)) 10
Locals
You can perform local bindings with the (let ) form. Inside the parens, the bindings are effective and immutable.
user> (let [a 23] (def a "cat") (- a 1)) 22
But a nested binding would shadow a binding in an outer scope:
user> (let [a 23] (+ a 1) (let [a "cat"] a)) "cat"
Loops, Recursive: see Recursive
I was wondering about the "recur" form -- why not just use recursion instead of bothering the user with a special case? Until it dawned on me that the JVM probably would have to support tail call optimization, which indeed it does not. TJoC opines that "recursion from a tail position is in many ways like a structured goto" -- quite so. But then again, it's maybe a good thing to have a specific form for tail calls so as to make more transparent what is going on.
user> (defn su [a] (loop [sum 0, a1 a] (if (seq a1) (recur (+ sum (first a1)) (rest a1)) sum))) #'user/su user> (su [1 2]) 3 user> (reduce + [1 2]) 3
Mordac The Preventer (Quoting)
To prevent evaluation of collections they can be quoted. Classical Lisp hat the quote fun, respectively the single-quote shorthand:
user> '(+ 1 2) (+ 1 2)
Syntax-quoting is a variant for Clojure that has some extra features, namely auto-qualification:
user> `(+ 1 2) (clojure.core/+ 1 2)
This can be handy I guess when persisting code across namespaces.
If some of the expression that you're syntax-quoting should exempted from the quote, ie. should be evaluated right away, there's the unquote form:
user> `(1 2 ~(+ 3 4)) (1 2 7)
To splice contents of a list into another without quoting, use the unquote-splice form ~@
user> (let [x '(2 3)] `(1 ~@x)) (1 2 3)
Automatically construct a uniqe symbol:
user> `sym# sym__699__auto__
Java (and others) Interop
Static class members access:
user> java.lang.Long/MAX_VALUE 9223372036854775807
Objects can be constructed with ClassName., and methods are accessed with funcalls of the form (.method object [args]).
Create a Java HashMap instance with keys "foo" and "bar, retrieve the value of "foo", update "foo" and retrieve again:
user> (def h (java.util.HashMap. {"foo" 23 "bar" 42})) #'user/h user> (.get h "foo") 23 user> (.put h "foo" "quux") 23 user> (.get h "foo") "quux"
The .. macro is convenience for chained method calls. Eg. for Java code like:
// java h.get("foo").endsWith("ux");
you can conveniently use:
user> (.. h (get "foo") (endsWith "ux")) true
The doto macro bundles a set of mutators and an object:
user> (doto h (.put "foo" 77) (.put "fuxor" "niyah"))
Anomalies
This is pretty directly taken over from Java, except there's no checked exception requirement, thank god.
Throwing:
user> (throw (Exception. "nooo!")) Exception nooo!...
Catching:
user> (try (#(/ 1 0 )) (catch ArithmeticException e "beware the singularity")) "beware the singularity"
Spaces
Namespaces are Clojures' way of modularizing code. You can declare a new namespace with the ns form. Any var created in a namespace is local to that namespace, as you'd expect. There's a handy built-in var *ns* that refers to the current namespace.
We're making a new namespace:
user> (ns foo.bar) nil foo.bar> *ns* #<Namespace foo.bar>
The var x is local to foo.bar:
foo.bar> (def x 23) #'foo.bar/x foo.bar> (ns foo.quux) nil foo.quux> x CompilerException java.lang.RuntimeException: Unable to resolve symbol: x ...
But we can refer to it with a fully qualified name:
foo.quux> foo.bar/x 23
And we can switch back as well:
foo.quux> (ns foo.bar) nil foo.bar> x 23
Works with funs as well:
foo.bar> (defn f [a] (* a a)) #'foo.bar/f foo.bar> (f 2) 4 foo.bar> (ns foo.quux) nil foo.quux> f CompilerException java.lang.RuntimeException: Unable to resolve symbol: f ... foo.quux> (foo.bar/f 4) 16
Loading Namespaces
Declaring a namespace and loading another library with an alias:
peter> (ns peter (:require [clojure.string :as st])) nil peter> (st/split-lines "foo \n bar") ["foo " " bar"]
Using a specific function from another library:
peter> (ns peter (:require [clojure.java.shell :refer (sh)])) nil peter> (sh "hostname" "-s") {:exit 0, :out "newton\n", :err ""}
Wrapping
This part started with functions, then touched reader features, scoping, quoting, Java interop, and namespaces. Quite intense. By now I think I might actually be able to DO something with Clojure.