Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clojure Q&A

Clojure Q&A

A work in progress; a new approach to teaching Clojure to relative novices.

Howard M. Lewis Ship

November 05, 2015

More Decks by Howard M. Lewis Ship

Other Decks in Technology


  1. a: Hopefully. He's been coding Clojure full time for several

    years. And he's done some interesting stuff before that. He's also written some well-favored open source libraries for Clojure.
  2. a: Yes, because "Zanzibar" is a string. In fact, it

    is an instance of java.lang.String. Clojure uses the underlying host's facilities where possible.
  3. a: Yes, because 5431 is a literal number. In fact,

    it is an instance of java.lang.Long.
  4. a: Yes, because true and false are booleans. In fact,

    it is an instance of java.lang.Boolean.
  5. a: No. I think you mean nil. Java's null is

    Clojure's nil. It's about heritage.
  6. a: Yes, because \J is a character. It is an

    instance of java.lang.Char. \newline, \tab, and a few others work as well. There's also Unicode character support.
  7. a: Yes, because john-brunner is a symbol. Clojure symbols start

    with a letter (or certain punctuation) and can contain most other characters.
  8. a: No. :science-fiction is a keyword. Keywords look like symbols,

    but start with a colon. Symbols or keywords can contain colons, but that is rarely used.
  9. a: Yes, because symbols can be namespace qualified. Symbols can

    contain a namespace, a slash, and a simple symbol. Watch out: simple symbols can start with a slash. clojure.core// is also a symbol.
  10. a: Yes, because ::donald-hogan starts with a colon. Two colons

    is a shortcut for qualifying the keyword into the current namespace.
  11. a: No. There's a couple of more literal types, such

    as regular expressions and ratios, and some more number types. There's also collection forms.
  12. a: Yes, because () is the empty list. In most

    cases, nil and () are treated identically but they aren't the same thing.
  13. a: Yes, because (1 2 3) is a sequence of

    forms surrounded by open and close parenthesis.
  14. a: Yes. Lists can contain a mix of different types

    of forms. Forms are separated by whitespace, including blank lines. The comma character is also whitespace in Clojure.
  15. a: No. [1 2 3] is a vector. Vectors are

    similar to lists, but support indexed access to the elements. We'll come back to that later.
  16. a: No. Curly braces denote a map form, but map

    forms must have an matching number of keys and values.
  17. a: Yes, because there is a value for every key.

    Keys and values can be any form, even collections. Remember: commas are whitespace. Some people like to put commas after each value.
  18. a: Yes. This is a set. Like the other collections,

    the values inside the set can be any other form, even collections or nil. Unlike lists or vectors, the values inside a set are un-ordered.
  19. a: No. It is a comment. Comments start with a

    ; and continue to the end of the line. By convention, a single ; is a comment for the same line. A ;; denotes a block within a definition. A ;;; denotes a section within a source file.
  20. a: It evaluates to [1 2 3]. Vectors, maps, and

    sets evaluate to themselves.
  21. a: A function, from the clojure.core namespace. = is a

    symbol, and symbols evaluate to something else. Everything in the clojure.core namespace is automatically available by default.
  22. a: () evaluates to itself, (). As we'll see, this

    is an exception to the general rule.
  23. a: It evaluates to false. A list form evaluates as

    a function call. The = function compares two or more values for equality.
  24. a: An error. Each function has an arity, or number

    of parameters it expects. It is an error to invoke a function with the wrong number of arguments. user=> (=) clojure.lang.ArityException: Wrong number of args (0) passed to: core/=
  25. a: false. The = function does not coerce its arguments,

    and the java.lang.Long 42 does not equal the java.lang.Double 42. You can use == instead; it only works for numbers and does some coercion.
  26. a: An error. The first form inside a list must

    be a function. user=> (1 2 3) java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn
  27. a: It evaluates to (flip flop). The leading quote prevents

    evaluation of the list. '(flip flop) ➠ (flip flop) The symbols are not evaluated and do not have to be previously defined.
  28. a: The Java Class java.lang.String. The classes in the java.lang

    package are automatically available. Other classes can be imported, or fully qualified (e.g., java.util.ArrayList).
  29. a: It evaluates to nil. More importantly, it has a

    side effect. user=> (println "Outstanding!") Outstanding! ➠ nil
  30. q: What does this evaluate to? (def book {:title "Stand

    on Zanzibar" :author "John Brunner"})
  31. a: It evaluates to a Var, a holder of references

    to other things. The Var prints itself as #'user/book. A side effect is that the current namespace, user, is modified: a new Var is created and added to the namespace.
  32. clojure.core = def defn String … user book class java.lang.String

    {:title "Stand on Zanzibar" :author "John Brunner"} a function a function a function
  33. a: It evaluates to: {:title "Stand on Zanzibar" :author "John

    Brunner"} This is the value stored in the Var identified by the book symbol.
  34. a: It evaluates to the Var #'user/add-one. It also adds

    the symbol mapping to the user namespace.
  35. clojure.core = def defn String … user book add-one class

    java.lang.String {:title "Stand on Zanzibar" :author "John Brunner"} a function a function a function a function
  36. q: What is the purpose of this string? (defn add-one

    "Returns the value of x incremented by one." [x] (+ 1 x))
  37. a: It evaluates to :science-theater. The let macro is a

    way to define local symbols. mystery is a local symbol, and becomes the evaluation of the let.
  38. a: It evaluates to 6000. mystery is a local symbol

    for an anonymous function. Passing 3000 to mystery returns 6000.
  39. a: It evaluates to "* YOW *". str is a

    function that assembles a string. #() is an even more terse way to write an anonymous function. The % symbol is the parameter.
  40. a: No, it is a reader macro. The reader is

    the part of Clojure that parses a stream of characters into forms. The #(…) sequence is converted into a (fn [x] …) form before being evaluated.
  41. a: It depends. Pure - mathematically pure - functions will

    always return the same value for the same parameters. Generally, Clojure functions are pure.
  42. • A function is pure if it only calls pure

    functions. • A function is pure if it performs no side-effects. • A pure function must not reference mutable state. • Input or output of any kind is impure: • Reading from the keyboard • Reading from a file, socket, etc. • System.currentTimeMillis(), etc. • Randomness is impure.
  43. a: Pure functions are easier to reason about. They tend

    to be simpler. You don’t have to think about anything outside the function’s body and parameters. You can test them easily, and experiment with them using the REPL. Pure functions compose better than impure functions.
  44. a: A pure function is independent from time. With multiple

    threads, it does not matter what threads are doing which calculations. Computing the result of a pure function can be deferred until the value is needed. The result can be cached, using the parameters as a key. This is called memoization.
  45. a: It depends. Java objects are often mutable. They contain

    fields whose value can be updated at any time. Mutability works against purity. Basically, any mutable field anywhere is an additional invisible parameter of the Java method. This makes reasoning about Java much harder.
  46. a: Yes, for vectors, maps, and sets. Clojure collections are

    persistent (aka immutable). They can act as functions. As functions, they are pure.
  47. a: It evaluates to :ford. A vector can acts as

    a function; the parameter is the index.
  48. a: It evaluates to "John Brunner". Matching keys return their

    value. Non-matching keys return nil, or the second parameter.
  49. a: It evaluates to :pinky. Sets return the matching value

    if found, or nil. This is idiomatic for testing for inclusion in the set.
  50. a: I'm not sure, I think it is related to

    beauty. Truthy is easier. nil and false are falsey. Anything else is truthy.
  51. q: Is odd? a predicate? (doc odd?) ------------------------- clojure.core/odd? ([n])

    Returns true if n is odd, throws an exception if n is not an integer
  52. a: Yes, because odd? is a 1-arity function that returns

    a truthy response. Predicates end in ? by convention.
  53. q: What does this evaluate to? (iterate inc 1) inc

    is a function that adds one to a number
  54. a: It evaluates to (1 2 3 4 5 …)

    iterate keeps passing a value through a function, building an infinite list from it, as if: (1 (inc 1) (inc (inc 1)) (inc (inc (inc 1))) …) Clojure handles infinite data really well via lazy evaluation.
  55. a: A filter. The filter function is a predicate. Each

    collection value is passed, in turn, to the predicate. Using filter, only the truthy predicate results are included in the result. remove is the opposite; truthy predicate results are removed, falsey are retained.
  56. a: It evaluates to 
 (1 3 5 7 9

    11 13 15 17 19). take will return up to N values from a collection. take and filter are lazy.
  57. a: Yes, so we would use the ->> ("thread last")

    macro. (->> (iterate inc 1) (filter odd?) (take 10)) This makes the flow of transformations easier to understand.
  58. a: A mapping operation. The provided function can convert one

    cube to one pyramid. f The map function converts a collection of cubes to a collection of pyramids.
  59. a: It evaluates to (6 11 51). map does not

    return a vector; this is a lazy list.
  60. q: What does this evaluate to? (take 10 (map *

    (iterate inc 1) (iterate inc 1))) * is, of course, multiplication.
  61. a: It evaluates to (1 4 9 16 25 36

    49 64 81 100). f The arity of the mapping function must match the number of collections passed to map.
  62. a: Yes. Mostly. A lazy collection’s internal state does mutate,

    as lazy values are computed. A lazy collection’s externally visible state is pure, as long as the mapping function (for map) or predicate (for filter and reduce) is pure. Of course, the values in the collection need to be pure as well.
  63. a: A race condition occurs when two different threads read

    and mutate the same memory, at the same time.
  64. a: Heck no! There are deadlocks where two or more

    threads just stop. Or … sometimes different threads see different values in the same mutable field. Or things can just get slow (because of locks).
  65. a: Yes. That internal mutable state is properly protected by

    locks. All threads will see a consistent view of the lazy collection.
  66. Some Coders (def coders [{:name "Rich" :language :clojure} {:name "Guido"

    :language :python} {:name "Bjarne" :language :c++} {:name "Charles" :language :forth} {:name "Brendan" :language :javascript}])
  67. a: (map :name coders) will evaluate to:
 ("Rich" "Guido" "Bjarne"

    "Charles" "Brendan") A keyword can act as a function for accessing a value from a map. Remember that map is a data structure, and map is a function for transforming collections.
  68. a: The output of one mapping operation can be the

    input to the next. (->> coders (map :name) (map #(subs % 0 1)) set) ➠ #{"G" "R" "C" "B"} subs extracts a substring from a string. set converts a collection to a set. Notice how "B" is only present once.
  69. a: A reducing operation. f The reducing function accepts an

    accumulator and a collection value, and returns a new accumulator. It reduces a collection to a single accumulated value.
  70. a: It evaluates to 4321 as follows: (+ 1 20)

    ➠ 21 (+ 21 300) ➠ 321 (+ 321 4000) ➠ 4321 The first value in the collection, 1, is treated as the initial accumulator.
  71. a: Lots of things, like deriving meaning from data. Example:

    finding the frequencies of specific values.
  72. (reduce (fn [m v] (update m v (fnil inc 0)))

    {} [1 3 5 10 5 3]) ➠{1 1, 3 2, 5 2, 10 1}
  73. 1. Start with an empty map 2. For each collection

    value: 1. Update the map, using the value as a key 2. Increment the map value (the count) 3. When the map value is nil, use 0 as default (reduce (fn [m v] (update m v (fnil inc 0))) {} [1 3 5 10 5 3])
  74. (require '[clojure.string :as str]) (def text "Any programming language can

    be divided into two parts: some set of fundamental operators that play the role of axioms, and the rest of the language, which could in principle be written in terms of these fundamental operators.\n") (def sep #"[\p{Space}|\p{Punct}]+") (->> (str/split text sep) (map count) frequencies) ➠ {7 2, 4 6, 6 1, 3 8, 2 8, 11 3, 9 3, 5 5, 8 2}