Study Topic: Language: Clojure
Jump to navigation
Jump to search
This is a Study Leave topic.
What Is It?
Clojure is a JWM hosted Lisp with access to the Java libraries.
Why Study It?
Might be better than Racket as a way into Lisp if I'm trying to get practical projects done - seems to have far more usage for things like RabbitMQ
Toggl code
Toggl code is LANG-CLOJURE
Deliverables
7 hours, then a write-up and taking stock as to next direction
Writeup
Books:
- Clojure Programming (has a terribly dense introductory section that's put me right off)
- Joy of Clojure (which seems like the better one to go for)
IDE:
- Started with Nightcode
- Now with Jetbrains Intellij IDEA and LaClojure plugin
Jar:
- at /Users/morrism/Documents/Study Leave/clojure-1.6.0
First impressions:
- Hmm. There is a Clojure Way that is *not* a Lisp Way. Or a Scheme Way. No TCO, not using lists as fundamental. I'm getting some problems fitting it in my head
- One thing everyone seems to agree on is you have to work within the Clojure idiom and not fight it
- There's a lot more syntax than the Scheme-like Racket I was looking at before
- I don't like the stack trace errors that the JVM brings forth
- IDE experience is not the best
Syntax Notes
Scalars:
- numbers
- M = arbitrary precision
- N = arbitrarily sized integers
- 0x = hex, 0 = octal, XXr = radix r (32r, 2r, etc)
- can have rationals
- symbols
- keywords
- :a, :b etc - can use namespace-resembling prefixes, e.g. :a/b
- strings
- characters: \a = lowercase a, \uXXXX for unicode
Collections:
- Vectors: [a b c]
- Lists: (a b c)
- Maps: {1 "one", 2 "two", 3 "three"}
- Can have hash-map, sorted-map, array-map
- Sets: #{1 2 "three' :four 0x05}
- Vectors (not lists) are the common Clojure structure.
- Use conj to add elements, pop to remove, peek to see final (not "last"!)
- Use clojure.lang.PersistentQueue for queue style collections
- Try to avoid code that behaves differently for different collection types
- "index' example on P112 shows how to construct uniform indexing
Bindings/Functions:
- (def name value)
- (defn make-set "Description" [x y] #{x y})
- multiple arities: (defn make-set ([x] #{x}) ([x y] #{x y}))
- in-place with #(): (def make-list2 #(list %1 %2))
Locals, loops, blocks:
- do: (do first next ... last), returns result of last
- let: (let [r 5 pi 3.14159 r-sq (* r r)] (* pi r-squared))
- recur: (defn sum-down-from [sum x] (if (pos? x) (recur (+ sum x) (dec x)) sum))
- loop: (defn sum-down-from [initial-x] (loop [sum 0, x initial-x] (if (pos? x) (recur (+ sum x) (dec x)) sum))
- recur always goes back to closest enclosing loop/dn. Needs to be in tail position.
Quoting:
- quote: (cons 1 '(2 3)) => (1 2 3)
- syntax-quote (backquote, `): `xxx: automatically qualifies all unqualified symbols
- unquote (tilde, ~): `(+ 10 ~(* 3 2)) => (clojure.core/+ 10 6)
- unquote splicing: (let [x '(2 3)] `(1 ~@x)) => (1 2 3)
Host libraries:
- By default java.lang is available
- (Math/sqrt 9) => 3.0
- Static properties: java.util.Locale/JAPAN => #<Locale ja_JP>
- New instances: can do (new java.awt.Point 0 1) but better to suffix dot (java.awt.Point. 0 1)
- Accessing members: dot+hyphen: (.-x (java.awt.Point. 10 20)) => 10
- Setting fields: set! thus: (let [origin (java.awt.Point. 0 0)] (set! (.-x origin) 15) (str origin)) => "java.awt.Point[x=15,y=0]"
- Chain using .. macro: (.. (java,util.Date.) toString (endsWith "2014"))
- "doto" macro: (doto (java.util.HashMap.) (.put "A" "vA") (.put "B" "valB"))
Exceptions:
- (throw (Exception. "message"))
- catching: (defn throw-catch [f] [(try (f) (catch ArithmeticException e "Arith") (catch Exception e (str "Msg" (.getMessage e))) (finally (println "returning...")))])
- no checked exceptions in Clojure
Namespaces:
- "ns" macro to create: (ns name1.name)
- variable *ns* is current namespace
- must load using :require / :refer
- (ns myname1.myname2 (:require clojure.set)) means can use e.g. (clojure.set/intersection ...)
- aliases: (ns myname1.myname2 (:require [clojure.set as s])) means can use e.g. (s/intersection ...)
- use :refer option with :require to map specified names into the namespace:
- (ns myname1.myname2 (:require [clojure.string :refer (capitalize)])) will just map capitalise into the namespace
- can also use :refer as a directiveinstead of :require to create mappings for libraries already loaded
- to use unqualified Java classes, use :import directive:
- (ns a.b (:import [java.util HashMap] [java.util.concurrent.atomic AtomicLong]))
- note that fully qualified Java class names are always available without any import
Immutability, Laziness:
- Clojure shares internals for immutable structures where possible
- Clojure supports laziness through a few constructs
- lazy-seq (use rest instead of next, and don't hold a ref to the head of the seq)
- can use infinite sequences, e.g. via (iterate op start)
- can use delay/force for call-by-need semantics