Clojure

Table of Contents

  • Index
  • Repository
  • 1 Clojure

    1.1 Overview

    Clojure is a functional programming lisp dialect that leverages the access to all Java platform libraries. It is based on Scheme, Common Lisp, ML and Haskell.

    Features:

    • Java API interface
    • Hosted on JVM (Java Virtual Machine)
    • Can compile to Java Bytecodes
    • Immutable data types
    • Very readable lisp syntax
    • Common lisp style macros
    • Functional Programming capabilities
    • Meta-programming capabilities
    • Namespace handling
    • Dynamic Typing

    Downsides:

    • Slow startup time.
    • Don't have tail recursion elimination, so loop must be used instead of recursion.
    • Low IDE support

    1.2 Toolset and Files Formats

    File Formats

       
    .clj Clojure Source Code.
    .class Compiled java byte code
    .jar Java archive - A zip file containing compiled java classes.
    .war Web Application to be deployed on servlet.
    pom.xml Project Object Model, stores project data and various configuration detail used by Maven to build the project

    Tools

    Tool Description
    Ant Tool to build java code, equivalent to Makefile in C
    Maven Build automation tool that can resolve dependecies.
    Lenigren Tool to automate, build, deployment and testing of Clojure code.

    Lein

       
    lein deps Installs dependencies in lib/ directory
    lein test <pred> Runs the project's tests, optionally filtered on PRED
    lein compile Ahead-of-time compiles into classes/
    lein repl Launches a REPL with the project classpath configured
    lein clean Removes all build artifacts
    lein jar Creates a jar of the project
    lein uberjar Creates a standalone jar with all dependencies
    lein pom Outputs a pom.xml file for interop with Maven
    lein install Installs in local repo (currently requires mvn)
    lein help <task> Displays tasks

    1.3 Basic Syntax

    1.3.1 Variables

    1. Global Variables
      user=> (def a 100.23)
      #'user/a
      user=> (def alist '(1 2 3 4 5))
      #'user/alist
      user=> (def my-string "Hello world Clojure")
      #'user/my-string
      user=> a
      100.23
      user=> my-string
      "Hello world Clojure"
      user=> alist
      (1 2 3 4 5)
      user=>
      
      
      user=> 
      user=> (def list-of-symbols '(sym1 symb2 sym3))
      #'user/list-of-symbols
      user=> list-of-symbols
      (sym1 symb2 sym3)
      user=>
      
    2. Lexical Bind (Local Variables)
       user=> (let [
                  a 10
                  b 20
                  c 100]
                  (+ a b c))
      130
      user=>
      

    1.3.2 Function Declaration

    1.3.2.1 Simple Function
     user=> (defn f [x y] (+ (* 3 x) (* 4 y)))
     #'user/f
    user=> 
    user=> (f 4 5)
    32
    user=> (f 5 6)
    39
    user=>
    
    1.3.2.2 Function With Docstring
    user=> (defn sqr [x]
             "Computes the square of a number"             
             (* x x))
     #'user/sqr
    user=> (sqr 10)
    100
    user=> (map sqr '(1 2 3 4 5 6))
    (1 4 9 16 25 36)
    user=>
    
    
    
    user=> (doc sqr)
     -------------------------
    user/sqr
    ([x])
      Computes the square of a number
    nil
    user=>
    
    1.3.2.3 Variadic Function

    Function with variable number of arguments:

    user=> (defn my-variadic-fun [& vars]
              (println vars))
    
    
    user=> 
    user=> (my-variadic-fun 1 2 3 4 'z 'w )
    (1 2 3 4 z w)
    nil
    
    user=> (my-variadic-fun 1 2 3 4 'z 'w :key)
    (1 2 3 4 z w :key)
    nil
    user=> 
    
    user=> (my-variadic-fun)
    nil
    
    user=> (defn variadic2 [x & vars]
            (do (println x)
              (println vars)))
    
    
    user=> 
    user=> (variadic2  2 'a 'b' 'c 'd)
    2
    (a b' c d)
    nil
    user=> 
    
    user=> (variadic2  10)
    10
    nil
    nil
    user=>
    
    1.3.2.4 Function with Optional arguments
    (defn fun-optional-c [a b & [c]]
        {:a a :b b :c c})
    
    user=> (fun-optional-c 1 2)
    {:a 1, :b 2, :c nil}
    
    user=> 
    user=> (fun-optional-c 1 2 3)
    {:a 1, :b 2, :c 3}
    user=>
    
    1.3.2.5 Function with Predefined Values
    (defn predefined-c-and-d [a b & {:keys [c d] :or {c 100 d 30}}]
        { :a a, 
          :b b,
          :c c,
          :d d}) 
    
    user=> (predefined-c-and-d 10 20)
    {:a 10, :b 20, :c 100, :d 30}
    user=> 
    
    user=> (predefined-c-and-d 10 20 :c 'c )
    {:a 10, :b 20, :c c, :d 30}
    user=> 
    
    user=> (predefined-c-and-d 10 20 :d -100 )
    {:a 10, :b 20, :c 100, :d -100}
    user=> 
    
    user=> (predefined-c-and-d 10 20 :c -90 :d -232 )
    {:a 10, :b 20, :c -90, :d -232}
    user=>
    
    1.3.2.6 Anonymous/ Lambda Functions
    (fn [<args>] (<body>))
    

    Anonymous Functions

    user=> (fn [x] (* x x))
    #object[user$eval369$fn__370 0x11bfe23 "user$eval369$fn__370@11bfe23"]
    user=>    
    
    user=> ((fn [x] (* x x)) 5)
    25
    user=> 
    
    user=> (map (fn [x] (* x x)) '(1 2 3 4 5))
    (1 4 9 16 25)
    user=> (map (fn [x] (* x x)) '[1 2 3 4 5])
    (1 4 9 16 25)
    user=> 
    
    
    user=> (map (fn [x] (* x x)) (range 1 10))
    (1 4 9 16 25 36 49 64 81)
    user=> 
    
    user=> ((fn [x y] (+ ( * 2 x) (* 3 y))) 10 20)
    80
    user=> 
    
    user=>  ((fn [a b c] (+ a b c)) 2 4 6)
    12
    user=> 
    
    ;;; Lambda functions can be stored in variables
    ;;;
    user=> (def f_xy  (fn [x y] (+ (* 2 x) (* 3 y))))
    #'user/f_xy
    user=> 
    user=> (f_xy 10 20)
    80
    user=> (f_xy 10 30)
    110
    user=>
    

    Lambda Function Macro

    Clojure has a pretty handy macro that allows to create lambda functions easily.

        user=> (#(+ 3 %) 4)
        7
        user=> (#(+ 3 %) 5)
        8
        user=> (map #(+ 3 %) '[1 2 3 4 5])
        (4 5 6 7 8)
        user=> 
    
    ;;; This is equivalent to
    ;;
        user=> (map (fn [x] (+ 3 x)) '[1 2 3 4 5])
        (4 5 6 7 8)
    
        user=> (Math/pow 2 3)
        8.0
    
    
        user=> (map #(Math/pow % 3) '[1 2 3 4 5 6])
        (1.0 8.0 27.0 64.0 125.0 216.0)
    
       ;; It is equivalent to:
    
        user=> (map (fn [x] (Math/pow x 3)) '[1 2 3 4 5 6])
        (1.0 8.0 27.0 64.0 125.0 216.0)
        user=> 
    
        ;;;;;;;
    
        user=> (map #(Math/pow 2 %) '[1 2 3 4 5 6])
        (2.0 4.0 8.0 16.0 32.0 64.0)    
    
        ;;;------------------------------------;;
    
    
    
        user=> #(+ (* 3 %1) (* 4 %2))
        #object[user$eval435$fn__436 0x79dfad "user$eval435$fn__436@79dfad"]
        user=> 
    
        ;; It is equivalent to
    
        user=> (fn [x y] (+ (* 3 x) (* 4 y)))
        #object[user$eval439$fn__440 0x1629510 "user$eval439$fn__440@1629510"]
        user=>     
    
        user=> ( #(+ (* 3 %1) (* 4 %2)) 4 5)
        32
        user=> 
    
        user=> (map #(+ (* 3 %1) (* 4 %2)) '[1 2 3 4 5] '[4 5 6 8 9])
        (19 26 33 44 51)
        user=>
    

    1.3.3 Predicates

    Type Checking Predicates

    Predicate Returns true for  
    symbol? (symbol? 'sym) Symbol
    string? (string? "something") String
    list? (list? '(1 2 3 4 5)) List, linked list
    vector? (vector? '[1 2 3 4]) Array
    map? (map? {:a 10 :b 20 :c 30}) Hash table
    number? (number? 2.23)  
    nil? (nil? nil) End of a list or java null value

    Special Predicates

    Predicate  
    true? Test if value is true
    false? Test if value is false
    nil? Test if value is nil (java null)
    empty? Test if list, vector or map(hash) is empty
    user=> (map symbol? '(symbol 1 2.323 "hello"))
    (true false false false)
    user=>
    
    
    user=> (map number? '(symbol 1 2.323 "hello"))
    (false true true false)
    user=>
    
    
    user=> (map string? '(symbol 1 2.323 "hello"))
    (false false false true)
    user=>
    
    
    user=> (map list? '(symbol 1 2.323 "hello" nil '() (1 2 3 4)))
    (false false false false false true true)
    user=>
    
    
    user=> (empty? {})
    true
    user=> (empty? '())
    true
    user=> (empty? '[])
    true
    user=> 
    
    user=> (nil? nil)
    true
    user=> (nil? {})
    false
    user=> (nil? '())
    false
    user=> (nil? '[])
    false
    user=>
    

    1.3.4 Data Types

    Symbol

    user=> (type 'sym)
    clojure.lang.Symbol
    user=> 
    
    user=> (class 'sym)
    clojure.lang.Symbol
    user=> 
    
    user=> 'this-as-symbol
    this-as-symbol
    
    user=> '(list of symbols)
    (list of symbols)
    user=> 
    
    user=> '[vector of symbols]
    [vector of symbols]
    user=>
    
    user=> [:a :b :c]
    [:a :b :c]
    
    
    user=> (keyword "hello")
    :hello
    user=>
    

    String

    user=> "a string"
    "a string"
    
    user=> (class "a string")
    java.lang.String
    
    user=> (type "a string")
    java.lang.String
    user=>
    

    Number

    user=> 0x1023
    4131
    user=> 
    
    user=> -10e3
    -10000.0
    user=> 
    
    user=> 
    user=> (type 1e3)
    java.lang.Double
    user=> (type 1000)
    java.lang.Long
    user=> (type 10)
    java.lang.Long
    user=> (class 1e3)
    java.lang.Double
    user=>
    

    List

    user=> 
    user=> (type '(a b c d e))
    clojure.lang.PersistentList
    user=> 
    user=> (class '(1 2 3 4 5))
    clojure.lang.PersistentList
    user=> 
    
    user=> (def xs '(1 2 3 4 5))
    #'user/xs
    user=> 
    
    user=> (first xs)
    1
    user=> (second xs)
    2
    user=> (rest xs)
    (2 3 4 5)
    user=> 
    
    ;; Get the nth element
    ;;------------------------
    user=> 
    user=> (nth xs 0)
    1
    user=> (nth xs 1)
    2
    user=> (nth xs 3)
    4
    user=> 
    
    
    ;; List Constructor Cons
    ;;
    user=> (cons 1 nil)
    (1)
    user=> (cons 1 (cons 2 (cons 3 nil)))
    (1 2 3)
    user=>
    

    Vector

    user=> (type [1 2 3 4])
    clojure.lang.PersistentVector
    user=> 
    
    user=> (class [1 2 3 4])
    clojure.lang.PersistentVector
    user=>
    
    user=> (nth [1 2 3 4] 0)
    1
    user=> (nth [1 2 3 4] 3)
    4
    user=> (first [1 2 3 4])
    1
    user=> (second [1 2 3 4])
    2
    user=> (rest [1 2 3 4])
    (2 3 4)
    user=>
    

    Hash Map / Hash Table

    ;; Create a Hash map
    ;;----------------------
    user=> 
    user=> (def params { :sn "10" :cn "" :locale "ptBR" :num 12345})
    #'user/params
    user=> (:keys params)
    nil
    
    ;; Get all keys
    ;;----------------------
    user=> (keys params)
    (:sn :cn :locale :num)
    user=> 
    user=> 
    
    ;; Select all values
    ;;----------------------
    user=> (vals params)
    ("10" "" "ptBR" 12345)
    user=>
    
    ;; Select a single key
    ;;----------------------
    user=> (get params :locale)
    "ptBR"
    user=> (get params :x)
    nil
    user=> (get params :x 'alternative)
    alternative
    user=> 
    
    ;; Select Multiple Keys
    ;;
    user=> (select-keys params [:cn :sn])
    {:cn "", :sn "10"}
    user=> (select-keys params [:cn :sn :dumy])
    {:cn "", :sn "10"}
    user=> 
    
    ;; Select the value from mmultiple keys 
    ;;
    (defn select-vals [params keys]
      (vals (select-keys params keys)))    
    
    user=> (select-vals {:a 123 :b 234 "hello" "world"} [:a "hello"])
    (123 "world")
    user=>     
    
    user=> (defn apply-vals [hmap f & ks]
              (apply f (select-vals hmap ks)))
    #'user/apply-vals
    user=> 
    user=> (apply-vals {:id 102 :price 10.4 :n 20} * :price :n)
    208.0
    user=> 
    
    ;;  Create a dictionary from two arrays
    ;;
    user=> (zipmap ["x" "y" "z"] [10 20 -30])
    {"x" 10, "y" 20, "z" -30}
    user=> 
    
    (defn format-params [params]
        (clojure.string/join "&"
                       (map #(format "%s=%s" %1 %2)
                            (keys params) (vals params))))
    
    user=> (format-params {"x" "1200" "y" 302 "user" "dummy"})
        "x=1200&y=302&user=dummy"
    

    1.3.5 String Functions

    1.3.5.1 Basic String Functions
    ;; Load clojure.string namespace as str 
    ;;
    (require '[clojure.string :as str])
    
    ;; List all functions in the namespace
    ;;
    ;;----------------------------
    user=> (dir clojure.string)
    blank?
    capitalize
    escape
    join
    lower-case
    re-quote-replacement
    replace
    replace-first
    reverse
    split
    split-lines
    trim
    trim-newline
    triml
    trimr
    upper-case
    nil
    user=>     
    
    
    user=> (str/split-lines  "line1\nline2\nline3")
    ["line1" "line2" "line3"]
    user=> 
    
    user=> (str/split-lines  "line1\rline2\rline3")
    ["line1\rline2\rline3"]
    user=> 
    
    user=> (str/replace "foo bar foobars" #"foo" "0x00121")
    "0x00121 bar 0x00121bars"
    user=>     
    
    user=> (str/join "," ["x" "y" "z"])
    "x,y,z"
    user=> 
    
    user=> (str/trim "   \n\n\nsome \n white space \n\n\n")
    "some \n white space"
    user=> 
    
    user=> (str/split "a,b,c,d," #",")
    ["a" "b" "c" "d"]
    user=> 
    
    
    user=> (str/join "-" ["a" "b" "c" "d"])
    "a-b-c-d"
    user=> 
    
    user=> (map str/blank? ["" "\n" "\r" "\r\n" " " "\t" "asdas"])
    (true true true true true true false)
    user=>
    
    1.3.5.2 String Parsing Functions

    Integer -> String

    user=> (Integer/parseInt "202323")
    202323
    
    ;; Scheme Notation: [from type]->[to type]
    ;;
    user=> (defn string->int [str] 
                (Integer/parseInt str))
    #'user/string->int
    user=> 
    user=> (string->int "10223232")
    10223232
    user=>
    

    Float -> String

    user=> (Float/parseFloat "2323.2323e-3")
    2.3232324
    user=> 
    
    (defn string->float [str]
        (Float/parseFloat str))
    

    1.3.6 Important Functions

    1.3.6.1 Special Functions

    Constantly

    user=> (map (constantly 10) '(1 2 3 4 5 6))
    (10 10 10 10 10 10)
    user=>
    

    Identity

    user=> (map identity '(a b c d 1 2 3 "hello" world))
    (a b c d 1 2 3 "hello" world)
    user=>
    

    Range

    user=> (range 1 10)
    (1 2 3 4 5 6 7 8 9)
    
    user=> (range 0 100 10)
    (0 10 20 30 40 50 60 70 80 90)
    
    1.3.6.2 Higher Order Functions

    Map

    user=> (map (fn [x] (* x x)) '(1 2 3 4 5 6))
    (1 4 9 16 25 36)
    user=> 
    
    user=> (map (fn [x y] (+ (* x x) (* y y))) '(1 2 3 4 5) '(3 5 7 8 9))
    (10 29 58 80 106)
    user=>
    

    Mapv

    Mapv works just like map, except that it returns a vector instead of a list.

    user=> (map #(* % 2) (range 10))
    (0 2 4 6 8 10 12 14 16 18)
    user=> 
    
    
    user=> (mapv #(* % 2) (range 10))
    [0 2 4 6 8 10 12 14 16 18]
    user=>
    

    Mapcat

    user=> 
    (defn single-double-triple [x]
      [(* x 1) (* x 2) (* x 3)])
    #'user/single-double-triple
    user=> 
    user=> (mapcat single-double-triple (range 10))
    (0 0 0 1 2 3 2 4 6 3 6 9 4 8 12 5 10 15 6 12 18 7 14 21 8 16 24 9 18 27)
    user=>
    

    For-each

    It is not defined in the standard library, however it is pretty useful function from scheme to map a function with side effect to a list or vector.

    (defn for-each [f xs] (doseq [x xs] (f x)))
    
    user=> (for-each println '[1 2 3 4 5 6])
    1
    2
    3
    4
    5
    6
    nil
    user=>
    

    Filter

    user=> (filter even? (range 1 20))
    (2 4 6 8 10 12 14 16 18)
    user=> 
    user=> (filter odd? (range 1 20))
    (1 3 5 7 9 11 13 15 17 19)
    user=>
    

    Apply

    Apply a function to a list of arguments

    user=> (defn f [x y] (+ (* 3 x) (* 4 y)))
    
    user=> 
    user=> (apply f '(7 8))
    53
    user=> 
    
    user=> (apply f '(7 8))
    53
    user=> (defn map-apply [f arglist] (map (fn [xs] (apply f xs)) arglist))
    #'user/map-apply
    user=> 
    user=> (map-apply f '((7 8) (3 4) (5 6)))
    (53 25 39)
    user=>
    

    Partial

    Partial - Partial application.

    user=> ((partial + 3) 4)
    7
    user=> (map (partial + 3) '(1 2 3 4 5 6))
    (4 5 6 7 8 9)
    user=>
    

    Comp

    Function Composition

    user=> ((comp (partial + 3) (partial * 4)) 10)
    43
    user=>
    
    
    user=> (map (comp (partial + 3) (partial * 4)) '(1 2 3 4 5 6))
    (7 11 15 19 23 27)
    user=>
    
    
       comp - Can be used to invert predicates
    
    user=> (filter (comp not zero?) [0 1 0 2 0 3 0 4])
    (1 2 3 4)
    user=>
    

    Juxt

    Apply a list of functions to a single argument.

    user=> (def f (juxt (partial + 3) (partial * 4) (partial * 5)))
    #'user/f
    user=> (f 5)
    [8 20 25]
    user=> (map f '(1 2 3 4 5))
    ([4 4 5] [5 8 10] [6 12 15] [7 16 20] [8 20 25])
    user=>
    
    
    user=> ((juxt + * min max) 3 4 6)
    [13 72 3 6]
    user=>
    

    dotimes

    user=> 
    (dotimes [x 10]
              (dotimes [y 10]
                (print (format "%3d " (* (inc x) (inc y)))))
              (println))
      1   2   3   4   5   6   7   8   9  10 
      2   4   6   8  10  12  14  16  18  20 
      3   6   9  12  15  18  21  24  27  30 
      4   8  12  16  20  24  28  32  36  40 
      5  10  15  20  25  30  35  40  45  50 
      6  12  18  24  30  36  42  48  54  60 
      7  14  21  28  35  42  49  56  63  70 
      8  16  24  32  40  48  56  64  72  80 
      9  18  27  36  45  54  63  72  81  90 
     10  20  30  40  50  60  70  80  90 100 
    nil
    user=>
    

    doseq

    user=> (doseq [x '[1 2 3 4 5]]  (println (+ 2 (* 3 x))))
    5
    8
    11
    14
    17
    nil
    user=> 
    
    user=> (doseq [x '[1 2 3]
                   y '[a b c d e]
                   ]
             (println (list x y)))
    (1 a)
    (1 b)
    (1 c)
    (1 d)
    (1 e)
    (2 a)
    (2 b)
    (2 c)
    (2 d)
    (2 e)
    (3 a)
    (3 b)
    (3 c)
    (3 d)
    (3 e)
    nil
    user
    
    1.3.6.3 Lazy Higher Order Functions

    Iterate

    The same as the higher order function iterate from Haskell

    user=> 
    user=> (take 5 (iterate (partial + 1) 0))
    (0 1 2 3 4)
    user=> 
    user=> (take 15 (iterate (partial + 1) 0))
    (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14)
    user=> 
    user=> (take 15 (iterate (partial * 2) 1))
    (1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384)
    user=>
    

    Repeat

    user=> (take 5 (repeat 10))
    (10 10 10 10 10)
    user=> (take 5 (repeat 5))
    (5 5 5 5 5)
    user=> (take 5 (repeat "a"))
    ("a" "a" "a" "a" "a")
    user=>
    

    Repeatedly

    Repeatedly works just like repeat, except that it takes a function instead of a value. It calls the function (which must take no arguments, and has side effects) repeatedly and returns a lazy sequence of its values.

    user=> (repeatedly 5 #(rand-int 500))
    (303 29 253 250 120)
    user=>
    

    1.4 List Comprehension

    user=> (for [x [1 2 3 4]] (* 3 x))
    (3 6 9 12)
    user=> 
    
    user=> (for [x [1 2 3 4], y [4 5 6]] (+ x y))
    (5 6 7 6 7 8 7 8 9 8 9 10)
    user=> 
    
    user=> (for [x [1 2 3 4], y [4 5 6]] (println {:x x, :y y :r (+ x y)}))
    ({:x 1, :y 4, :r 5}
    {:x 1, :y 5, :r 6}
    {:x 1, :y 6, :r 7}
    nil nil {:x 2, :y 4, :r 6}
    {:x 2, :y 5, :r 7}
    {:x 2, :y 6, :r 8}
    nil nil nil {:x 3, :y 4, :r 7}
    {:x 3, :y 5, :r 8}
    {:x 3, :y 6, :r 9}
    nil nil nil {:x 4, :y 4, :r 8}
    {:x 4, :y 5, :r 9}
    {:x 4, :y 6, :r 10}
    nil nil nil nil)
    
    
    ;;; List comprehension with guards
    ;;
    
    user=> (for [x (range 20)  :when (= (mod x 3) 0)]  x )
    (0 3 6 9 12 15 18)
    user=> 
    user=>    
    
    user=> (for [x (range 20)  :while (< x 10)]  x )
    (0 1 2 3 4 5 6 7 8 9)
    user=> 
    user=> 
    
    
    user=> 
    (for [ c (range 1 30)
          a (range 1 (+ 1 c))
          b (range 1 (+ 1 a))
          :when (= (+ (* a a) (* b b)) (* c c))] 
    
          [a b c])
    ([4 3 5] [8 6 10] [12 5 13] [12 9 15] 
    [15 8 17] [16 12 20] [20 15 25] 
    [24 7 25] [24 10 26] [21 20 29])
    user=>
    

    See also:

    1.5 S-expression parser, Serializer and Interpreter

    eval

    Evaluates, interprets a S-expression.

    user=> (eval '(+ 1 2 3 4 5))
    15
    user=> (eval '(Math/exp 3))
    20.085536923187668
    user=>
    

    load

    Evaluates, interprets a file containing clojure code, S-expressions.

    (load <filename.clj>)
    

    read-string

    Parses a S-expression.

    user=> (read-string "(Math/exp 3)")
    (Math/exp 3)
    user=> 
    
    
    user=> (eval (read-string "(Math/exp 3)"))
    20.085536923187668
    user=> 
    
    user=> (read-string "{:a 10, :b 20, :c [1 a b c d]}")
    {:a 10, :b 20, :c [1 a b c d]}
    user=>
    

    load-string

    Evaluates a string.

    user=> (def code "(println \"(+ 2 2) =\" ) (+ 2 2)")
    #'user/code
    
    user=> code
    "(println \"(+ 2 2) =\" ) (+ 2 2)"
    
    user=> (println code)
    (println "(+ 2 2) =" ) (+ 2 2)
    nil
    
    user=> (load-string code)
    (+ 2 2) =
    4
    user=>
    

    pr-str

    Serialize a s-expression to string.

    user=> (pr-str [1 2 3 4 5])
    "[1 2 3 4 5]"
    
    user=> (pr-str {:a 10 :b 20 :c '[1 a b c d]})
    "{:a 10, :b 20, :c [1 a b c d]}"
    user=> 
    
    user=> (pr-str '(def f [x] (+ x 3))
    )
    "(def f [x] (+ x 3))"
    user=>
    

    1.6 IO / Input and Output

    Print

    Print in the current line.

    user=> (print "Hello world")
    Hello worldnil
    user=>
    

    Println

    Print in a new line.

    user=> (println '[1 2 3 sym1 sym2 sym3 "str"])
    [1 2 3 sym1 sym2 sym3 str]
    nil
    
    (do 
         (println "Multiple")
         (println "Line")
         (println "printing")
         (println 'a))
    Multiple
    Line
    printing
    a
    nil
    user=>
    

    Slurp

    Documentation

    Read file, internet protocol like http, ftp …

    user=> (println (slurp "/etc/host.conf"))
    # The "order" line is only used by old versions of the C library.
    order hosts,bind
    multi on
    
    nil
    user=> 
    
    user=> (slurp "http://httpbin.org/user-agent")
    "{\n  \"user-agent\": \"Java/1.8.0_51\"\n}\n"
    user=>
    
    user=> (println (slurp "http://httpbin.org/user-agent"))
    {
      "user-agent": "Java/1.8.0_51"
    }
    
    nil
    user=>
    

    Spit

    Documentation

    Write a file.

    user=> 
    user=> (spit "/tmp/filetest.txt" "hello world clojure")
    nil
    user=> (slurp "/tmp/filetest.txt")
    "hello world clojure"
    user=>
    

    1.7 Documentation / Docstring and Reflection, Instrospection

    Show docstring

    user=> (doc apply)
    -------------------------
    clojure.core/apply
    ([f args] [f x args] [f x y args] [f x y z args] [f a b c d & args])
      Applies fn f to the argument list formed by prepending intervening arguments to args.
    nil
    user=> 
    
    user=> (doc Math/sin)
    nil
    user=>
    

    Retrive Source Code

    user=> (source reverse)
    (defn reverse
      "Returns a seq of the items in coll in reverse order. Not lazy."
      {:added "1.0"
       :static true}
      [coll]
        (reduce1 conj () coll))
    nil
    user=>
    

    Show all functions in a name space

    user=> (dir clojure.string)
    blank?
    capitalize
    escape
    join
    lower-case
    re-quote-replacement
    replace
    replace-first
    reverse
    split
    split-lines
    trim
    trim-newline
    triml
    trimr
    upper-case
    nil
    

    Inspect a Table

    user=> (require 'clojure.inspector)
    nil
    
    user=> (clojure.inspector/inspect-table '((1 2 3) (a b c) (e f g)))
    

    Clojure_Inspector.png

    1.8 Macros

    Clojure macros are based on Common Lisp macros.

    1.8.1 Quasi quote

    1.8.2 Macros by Example

    Print all forms inside the parenthesis

    (defmacro $p [func & args]
      `(println (~func ~@args)))
    
    
    user=> (slurp "http://httpbin.org/get")
    "{\n  \"args\": {}, \n  \"headers\": {\n    \"Accept\": \"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"Java/1.8.0_51\"\n  }, \n  \"origin\": \"183.173.124.2\", \n  \"url\": \"http://httpbin.org/get\"\n}\n      
    
    
    user=> ($p slurp "http://httpbin.org/get")
    {
      "args": {}, 
      "headers": {
        "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", 
        "Host": "httpbin.org", 
        "User-Agent": "Java/1.8.0_51"
      }, 
      "origin": "183.173.124.2", 
      "url": "http://httpbin.org/get"
    }
    
    ;; Macro expansion
    ;; 
    user=> (macroexpand '($p slurp "http://httpbin.org/get"))
    (clojure.core/println (slurp "http://httpbin.org/get"))
    user=>
    

    Invert a boolean value from a s-expression

    (defmacro $n [func & args]
      `(not (~func ~@args)))
    
    user=> (def x 10)
    #'user/x
    user=> 
    
    user=> (or (> 3 x) (= 5 x) (< 15))
    true
    
    user=> ($n or (> 3 x) (= 5 x) (< 15))
    false
    user=> 
    
    user=> (macroexpand '($n or (> 3 x) (= 5 x) (< 15)))
    (clojure.core/not (or (> 3 x) (= 5 x) (< 15)))
    user=>
    

    Debugging Injection Macro

    (defmacro $d [func & args]
      `(let
           [p# (~func ~@args)]
         (do
           (println (str (quote (~func ~@args)) " = " p#))
           p#)))
    
    user=> (+ 10 ($d + 3 ($d * 3 4)  ($d * 1 2 3 4 5)))
    (* 3 4) = 12
    (* 1 2 3 4 5) = 120
    (+ 3 ($d * 3 4) ($d * 1 2 3 4 5)) = 135
    145
    
    
    user=> (defn pyth [ x y ] ($d * ($d * x x) ($d * y y)))
    #'user/pyth
    user=> (pyth 4 5)
    (* x x) = 16
    (* y y) = 25
    (* ($d * x x) ($d * y y)) = 400
    400
    user=>
    

    Infix to Postfix operator

    (defmacro $ [a op b]
      `(~op ~a ~b))
    
    user=> ($ 10 + 20)
    30
    user=> ($ 3 * 10)
    30
    user=> (macroexpand '($ 10 + 20))
    (+ 10 20)
    user=>      
    
    user=> (def x 10)
    #'user/x
    user=> 
    user=> ($ ($ 3 > x) or ($ x < 15))
    true
    

    See also:

    1.9 Java Interoperability

    One of greatest advantages of Clojure to other Lisp's dialects is the Java interoperabilty that allows the user to use the full power of Java ecosystem.

    1.9.1 Java API Access

    user=> 
    user=>  (System/getProperty "java.vm.version")
    "25.51-b03"
    user=>  (System/getProperty "java.home")
    "/opt/java/jre"
    user=>  (System/getProperty "java.runtime.name")
    "Java(TM) SE Runtime Environment"
    user=>  (System/getProperty "java.vm.name")
    "Java HotSpot(TM) Server VM"
    user=>  (System/getProperty "java.vm.vendor")
    "Oracle Corporation"
    user=> 
    
    user=> (map #(System/getProperty %) 
    '("java.vm.vendor" 
    "java.home" 
    "java.runtime.name" 
    "java.vm.name"))
    
    ("Oracle Corporation" 
    "/opt/java/jre" 
    "Java(TM) SE Runtime Environment" 
    "Java HotSpot(TM) Server VM")
    user=>
    

    1.9.2 Call Class Static Method

    ;;; java.lang.Math.log10(10);
    ;;
    user=> (java.lang.Math/log10 100)
    2.0
    
    user=> (map java.lang.Math/log10 '[1 10 100 1000])
    CompilerException java.lang.RuntimeException: Unable to find static field: log10 in class java.lang.Math, compiling:(NO_SOURCE_PATH:28:1) 
    user=> 
    
    user=> (map #(java.lang.Math/log10 %) '[1 10 100 1000])
    (0.0 1.0 2.0 3.0)
    user=> 
    
    user=> 
    user=> (def log10 #(java.lang.Math/log10 %))
    #'user/log10
    user=> 
    user=> (map log10  '[1 10 100 1000])
    (0.0 1.0 2.0 3.0)
    user=> 
    
    ;;; Or Just: 
    
    user=> (def log10 #(Math/log10 %))
    #'user/log10
    user=> 
    user=> (map log10  '[1 10 100 1000])
    (0.0 1.0 2.0 3.0)
    user=>
    

    1.9.3 Java Constructor and Methods

    ;;; Create a new data object
    ;;
    user=> (new java.util.Date)
    #inst "2015-10-02T20:27:21.801-00:00"
    user=> 
    
    user=> 
    user=> (def today (new java.util.Date))
    #'user/today
    user=> today
    #inst "2015-10-02T20:27:50.289-00:00"
    user=> 
    user=> (.toString today)
    "Fri Oct 02 17:27:50 BRT 2015"
    user=> 
    
    user=> 
    user=> (.getMonth today) ;;; today.getMonth()
    9
    user=> (.getYear today)  ;;; today.getYear()
    115
    
    user=> (+ 1900 (.getYear today)) ;; 1900 + today.getYear()
    2015
    user=>     
    
    user=> (.getDay today)
    5
    user=>     
    
    ;;  Create a function that returns the current date object.
    ;;
    user=> (defn today-date [] (new java.util.Date))
    #'user/today-date
    user=> 
    user=> (today-date)
    #inst "2015-10-02T20:28:51.794-00:00"
    user=> 
    
    
    ;; The macro (memfn <method>) invokes a object method
    ;;
    
    user=> ((memfn getMonth) today)
    9
    user=> 
    
    
    ;; Invoke the method .toString()
    ;;
    ;;
    user=> (def obj->string  (memfn toString))
    #'user/obj->string
    user=> 
    
    user=> (obj->string today)
    "Fri Oct 02 17:28:57 BRT 2015"
    user=> 
    
    
    ;; Invoke multiple methods at same time
    ;; 
    user=>  ((juxt (memfn getMonth) (memfn getYear) (memfn getYear)) today)
    [9 115 115]
    user=>
    

    1.9.4 Doto Macro

    1.9.4.1 Doto Macro Expansion

    Without Doto Macro

    user=> (def j (new java.util.HashMap))
    #'user/j
    user=> j
    {}
    user=> (.put j "a" 100)
    nil
    user=> (.put j "b" "Hello world")
    nil
    user=> j
    {"a" 100, "b" "Hello world"}
    user=>
    

    With Doto Macro

    user=> (def j  
                (doto (new java.util.HashMap)
                  (.put "a" 100)
                  (.put "b" "Hello world")))
    #'user/j
    user=> j
    {"a" 100, "b" "Hello world"}
    user=>
    
    1.9.4.2 Simple GUI
    user=> 
    (doto (javax.swing.JFrame.)
      (.setLayout (java.awt.GridLayout. 2 2 3 3))
      (.add (javax.swing.JTextField.))
      (.add (javax.swing.JLabel. "Enter some text"))
      (.setSize 300 80)
      (.setVisible true))
    

    simplegui.png

    1.9.4.3 Java Import

    Import Packages

    ;; import java.util.Date,  java.util.Stack
    ;; import java.net.Proxy,  java.net.URI
    ;;
    user=> (import [java.util Date Stack] [java.net Proxy URI])
    java.net.URI
    user=> (Date.)
    #inst "2015-09-16T01:39:21.365-00:00"
    user=>
    
    user=> (java.util.Date.)
    #inst "2015-09-16T18:11:15.686-00:00"
    user=> 
    user=> (new java.util.Date)
    #inst "2015-09-16T18:11:21.166-00:00"
    user=>
    

    Import Specific Packages from Java Libraries

    user=> 
    user=> (import '(java.util Date GregorianCalendar))
    java.util.GregorianCalendar
    user=> 
    user=> Date
    java.util.Date
    user=> 
    user=> GregorianCalendar
    java.util.GregorianCalendar
    user=> 
    user=> (new Date)
    #inst "2015-09-16T18:12:46.308-00:00"
    user=> 
    user=> (new GregorianCalendar)
    #inst "2015-09-16T15:12:54.212-03:00"
    user=>
    

    Access Constans in a Java Class

    user=> (. java.util.Calendar APRIL)
    3
    user=> java.util.Calendar/APRIL
    3
    user=>
    
    
    user=> (import [java.util Calendar])
    java.util.Calendar
    
    user=> 
    user=> (. Calendar APRIL)
    3
    user=>
    

    1.9.5 Classpath

    Print Class Path

    user=> (System/getProperty "java.class.path")
    "/opt/clojure.jar:/home/tux/PycharmProjects/clojure"
    user=>
    
    
    user=> (println (seq (.getURLs (java.lang.ClassLoader/getSystemClassLoader))))
    (#object[java.net.URL 0x1ee81fc file:/opt/clojure.jar] #object[java.net.URL 0x2aa843 file:/home/tux/PycharmProjects/clojure/])
    nil
    

    1.9.6 Load Jar file at run time

    (defn load-jar [jarfile]
         (.addURL
         (.getContextClassLoader (Thread/currentThread))
         (.toURL (.toURI (new java.io.File jarfile)))))
    
    (load-jar "<jarfile.jar>")
    (import <jarfile-class...>)
    

    1.9.7 Java Instrospection

    user=> (.getMethods java.util.Date)
    #object["[Ljava.lang.reflect.Method;" 0x826f61 "[Ljava.lang.reflect.Method;@826f61"]
    user=> 
    
    
    user=> (take 4 (.getMethods java.util.Date))
    (#object[java.lang.reflect.Method 0x15fac5f "public void java.util.Date.setTime(long)"] #object[java.lang.reflect.Method 0x12793 "public long java.util.Date.getTime()"] #object[java.lang.reflect.Method 0x15572a7 "public static java.util.Date java.util.Date.from(java.time.Instant)"] #object[java.lang.reflect.Method 0x1d94571 "public int java.util.Date.getYear()"])
    user=> 
    
    user=> (map (memfn toString) (take 4 (.getMethods java.util.Date)))
    ("public void java.util.Date.setTime(long)" "public long java.util.Date.getTime()" "public static java.util.Date java.util.Date.from(java.time.Instant)" "public int java.util.Date.getYear()")
    user=> 
    
    user=> (defn for-each [f xs] (doseq [x xs] (f x)))
    #'user/for-each
    user=> 
    
    user=> (for-each println (map (memfn toString) (take 4 (.getMethods java.util.Date))))
    public void java.util.Date.setTime(long)
    public long java.util.Date.getTime()
    public static java.util.Date java.util.Date.from(java.time.Instant)
    public int java.util.Date.getYear()
    nil
    user=> 
    
    user=> (for-each println (map (memfn toString) (seq (.getMethods java.util.Date))))    
    public void java.util.Date.setTime(long)
    public long java.util.Date.getTime()
    public static java.util.Date java.util.Date.from(java.time.Instant)
    public int java.util.Date.getYear()
    public int java.util.Date.getMonth()
    public int java.util.Date.getDate()
    public int java.util.Date.getHours()
    public int java.util.Date.getMinutes()
    public int java.util.Date.getSeconds()
    public static long java.util.Date.UTC(int,int,int,int,int,int)
    public void java.util.Date.setYear(int)
    public void java.util.Date.setMonth(int)   
    ...
    
    (defn get-methods [java-class]
        (map (memfn toString) 
            (seq ((memfn getMethods) java-class))))
    
    
    (defn show-methods [java-class]
        (for-each println (get-methods java-class)))
    
    user=> (get-methods java.util.Date)
    ("public void java.util.Date.setTime(long)" "public long java.util.Date.getTime()" "public static java.util.Date java.util.Date.from(java.time.Instant)" "public int java.util.Date.getYear()" "public int java.util.Date.getMonth()" "public int java.util.Date.getDate()" "public int java.util.Date.getHours()" "public int java.util.Date.getMinutes()" "public int java.util.Date.getSeconds()" "public static long java.util.Date.UTC(int,int,int,int,int,int)" "public void java.util.Date.setYear(int)" "public void java.util.Date.setMonth(int)"
    
    
    user=> (show-methods java.util.Date)
    
    public void java.util.Date.setTime(long)
    public long java.util.Date.getTime()
    public static java.util.Date java.util.Date.from(java.time.Instant)
    public int java.util.Date.getYear()
    public int java.util.Date.getMonth()
    public int java.util.Date.getDate()
    public int java.util.Date.getHours()
    public int java.util.Date.getMinutes()
    public int java.util.Date.getSeconds()
    ...
    
    user=> (def m (get-methods java.util.Date))
    #'user/m
    user=>
    
    ;;; Show the methods in the GUI
    ;;
    user=> (require 'clojure.inspector)
    user=> (clojure.inspector/inspect-tree m)
    

    Clojure_Inspector_methods.png

    ;;; Get the name of a class
    ;;
    user=> (.getName java.util.Date)
    "java.util.Date"
    
    ;; Get the class name of a class instance.
    ;;
    user=> (.getClass (new java.util.Date))
    java.util.Date
    user=> 
    
    ;; Get Constructors
    ;;
    (user=> 
    user=> (map (memfn toString) (seq (.getConstructors java.util.Date)))
    ("public java.util.Date()" "public java.util.Date(long)" "public java.util.Date(int,int,int,int,int)" "public java.util.Date(int,int,int,int,int,int)" "public java.util.Date(java.lang.String)" "public java.util.Date(int,int,int)")
    user=> 
    
    
    ;; Get Fields
    ;;
    ;;
    user=> (map (memfn toString) (take 5 (.getFields javax.swing.JFrame)))
    ("public static final int javax.swing.JFrame.EXIT_ON_CLOSE" "public static final int javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE" "public static final int javax.swing.WindowConstants.HIDE_ON_CLOSE" "public static final int javax.swing.WindowConstants.DISPOSE_ON_CLOSE" "public static final int javax.swing.WindowConstants.EXIT_ON_CLOSE")
    user=>
    

    1.9.8 Miscellaneous

    Examples about calling Java API in Clojure.

    1.9.8.1 Parse Date
    user=> (.parse (new java.text.SimpleDateFormat "dd/MM/yyyy") "03/01/2013")
    #inst "2013-01-03T03:00:00.000-00:00"
    user=> 
    user=> 
    
    
    (defn date-parser [date-format date-str]
      (.parse (new java.text.SimpleDateFormat date-format)
              date-str))
    
    (defn date-dmy [sep date-str]
      (date-parser (str "dd" sep "mm" sep "yyyy") date-str))
    
    (defn date-ymd [sep date-str]
      (date-parser (str "yyyy" sep "mm" sep "dddd") date-str))
    
    
    (defn date-mdy [sep date-str]
      (date-parser (str "mm" sep "dd" sep "yyyy") date-str))
    
    user=> (date-dmy "/" "04/01/2013")
    #inst "2013-01-04T03:01:00.000-00:00"
    user=>
    
    1.9.8.2 Get String from Clipboard

    Java code:

    import java.awt.Toolkit;
    import java.awt.datatransfer.Clipboard;
    import java.awt.datatransfer.DataFlavor;
    
    public class GetStringFromClipboard {
    
        public static void main(String[] args) throws Exception {
    
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Clipboard clipboard = toolkit.getSystemClipboard();
            String result = (String) clipboard.getData(DataFlavor.stringFlavor);
            System.out.println("String from Clipboard:" + result);
        }
    
    }
    

    Clojure Code;

    (import java.awt.Toolkit)
    (import [java.awt.datatransfer Clipboard DataFlavor])
    
    (defn getClipboard [] 
        (->
         (Toolkit/getDefaultToolkit)
         .getSystemClipboard
         (.getData DataFlavor/stringFlavor)))
    
    1.9.8.3 Http POST/GET

    Java Code: How to send HTTP request GET/POST in Java

    There is a Clojure library for this: clj-http

    ( import java.io.BufferedReader )
    ( import java.io.IOException )
    ( import java.io.InputStreamReader )
    ( import java.io.OutputStream )
    ( import java.net.HttpURLConnection )
    ( import java.net.URL )
    
    (defn buffread->string [buff]
      (clojure.string/join "\n"
                           (line-seq buff)))
    
    (defn format-params [params]
      (clojure.string/join "&"
                           (map #(format "%s=%s" %1 %2)
                                (keys params) (vals params))))
    
    
    (defn http-get
      "
       http-get <url> [:user-agent <user-agent string] [:params {key: value}]
    
      "
    
      [url & {:keys [user-agent params]
                            :or { user-agent "",  params {}}}]                 
      (let
          [
           conn (doto (.openConnection (new URL url))
                 (.setRequestMethod "GET") 
                 (.setRequestProperty "User-Agent" user-agent)
                 (.setDoOutput false))
    
    
           in   (new BufferedReader
                     (new InputStreamReader
                          (.getInputStream conn)))
           ]
      (if (not (= (.getResponseCode conn) 200))
        nil ;; Request has failed
        (buffread->string in))))
    
    (defn http-post
      "
       http-post <url> [:user-agent <user-agent string] [:params {key: value}]
    
      "
      [url & {:keys [user-agent params]
                            :or { user-agent "",  params {}}}]                 
      (let
          [
           conn (doto (.openConnection (new URL url))
                 (.setRequestMethod "POST") 
                 (.setRequestProperty "User-Agent" user-agent)
                 (.setDoOutput true))
    
    
           os  (doto (.getOutputStream conn)
                 (.write (.getBytes (format-params params)))
                 (.flush)
                 (.close))
    
           in   (new BufferedReader
                     (new InputStreamReader
                          (.getInputStream conn)))
           ]
      (if (not (= (.getResponseCode conn) 200))
        nil ;; Request has failed
        (buffread->string in))))
    
    
    user=> (println (http-get "http://httpbin.org/headers" :user-agent "http://httpbin.org/get"))
    {
      "headers": {
        "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", 
        "Host": "httpbin.org", 
        "User-Agent": "http://httpbin.org/get"
      }
    }
    nil
    user=> 
    
    user=> (println (http-post "http://httpbin.org/post" :params {"user" "dummy" "name" "john"}))
    
    {
      "args": {}, 
      "data": "", 
      "files": {}, 
      "form": {
        "name": "john", 
        "user": "dummy"
      }, 
      "headers": {
        "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", 
        "Content-Length": "20", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "User-Agent": "FIREFOX"
      }, 
      "json": null, 
      "url": "http://httpbin.org/post"
    }
    nil
    
    
    user=> (println (http-post "http://httpbin.org/post" :params {"currency" "BRL" "country" "BRAZIL" "lang" "pt-BR"}))
    {
      "args": {}, 
      "data": "", 
      "files": {}, 
      "form": {
        "country": "BRAZIL", 
        "currency": "BRL", 
        "lang": "pt-BR"
      }, 
      "headers": {
        "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", 
        "Content-Length": "38", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "User-Agent": ""
      }, 
      "json": null, 
      "method": "POST", 
      "origin": "187.113.121.52", 
      "url": "http://httpbin.org/post"
    }
    nil
    user=>
    

    1.10 Resources

    Author: nobody

    Created: 2018-06-17 Sun 02:38

    Emacs 25.3.1 (Org mode 8.2.10)

    Validate