Study Topic: Language: Clojure

From Matt Morris Wiki
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:

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