├── script ├── run.clj ├── test.clj ├── repl ├── run ├── test ├── test.bat ├── run.bat ├── repl.bat └── deploy.sh ├── project.clj ├── ideaboard.txt ├── src └── koans │ ├── 05_sets.clj │ ├── 20_java_interop.clj │ ├── 11_lazy_sequences.clj │ ├── 21_partition.clj │ ├── 18_quote.clj │ ├── 17_atoms.clj │ ├── 04_vectors.clj │ ├── 07_functions.clj │ ├── 01_equalities.clj │ ├── 12_sequence_comprehensions.clj │ ├── 13_creating_functions.clj │ ├── 09_higher_order_functions.clj │ ├── 03_lists.clj │ ├── 16_refs.clj │ ├── 08_conditionals.clj │ ├── 24_macros.clj │ ├── 22_group_by.clj │ ├── 10_runtime_polymorphism.clj │ ├── 19_datatypes.clj │ ├── 14_recursion.clj │ ├── 23_meta.clj │ ├── 15_destructuring.clj │ ├── 06_maps.clj │ └── 02_strings.clj ├── Vagrantfile ├── .gitignore ├── README.md ├── resources └── koans.clj └── epl-v10.html /script/run.clj: -------------------------------------------------------------------------------- 1 | (require 'koan-engine.runner) 2 | (koan-engine.runner/exec "run") 3 | -------------------------------------------------------------------------------- /script/test.clj: -------------------------------------------------------------------------------- 1 | (require 'koan-engine.runner) 2 | (koan-engine.runner/exec "test") 3 | -------------------------------------------------------------------------------- /script/repl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | CLASSPATH=src 3 | 4 | for f in lib/*.jar lib/dev/*.jar resources/; do 5 | CLASSPATH=$CLASSPATH:$f 6 | done 7 | 8 | if [ "$OSTYPE" = "cygwin" ]; then 9 | CLASSPATH=`cygpath -wp $CLASSPATH` 10 | fi 11 | 12 | java -Xmx1G -cp $CLASSPATH jline.ConsoleRunner clojure.main 13 | -------------------------------------------------------------------------------- /script/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | CLASSPATH=src 3 | 4 | for f in lib/*.jar lib/dev/*.jar resources/; do 5 | CLASSPATH=$CLASSPATH:$f 6 | done 7 | 8 | if [ "$OSTYPE" = "cygwin" ]; then 9 | CLASSPATH=`cygpath -wp $CLASSPATH` 10 | fi 11 | 12 | java -cp "$CLASSPATH" clojure.main script/run.clj 13 | echo 14 | -------------------------------------------------------------------------------- /script/test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | CLASSPATH=src 3 | 4 | 5 | for f in lib/*.jar lib/dev/*.jar resources/; do 6 | CLASSPATH=$CLASSPATH:$f 7 | done 8 | 9 | if [ "$OSTYPE" = "cygwin" ]; then 10 | CLASSPATH=`cygpath -wp $CLASSPATH` 11 | fi 12 | 13 | java -cp "$CLASSPATH" clojure.main script/test.clj 14 | echo 15 | -------------------------------------------------------------------------------- /script/test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setLocal EnableDelayedExpansion 3 | 4 | set CLASSPATH=" 5 | for %%j in (".\lib\*.jar", ".\lib\dev\*.jar") do ( 6 | set CLASSPATH=!CLASSPATH!;%%~fj 7 | ) 8 | set CLASSPATH=!CLASSPATH!" 9 | set CLASSPATH=%CLASSPATH%;src;resources 10 | 11 | java -Xmx1G -cp %CLASSPATH% clojure.main script\test.clj 12 | -------------------------------------------------------------------------------- /script/run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setLocal EnableDelayedExpansion 3 | 4 | set CLASSPATH=" 5 | for %%j in (".\lib\*.jar", ".\lib\dev\*.jar") do ( 6 | set CLASSPATH=!CLASSPATH!;%%~fj 7 | ) 8 | set CLASSPATH=!CLASSPATH!" 9 | set CLASSPATH=%CLASSPATH%;src;resources 10 | 11 | java -Xmx1G -cp %CLASSPATH% clojure.main script\run.clj 12 | 13 | -------------------------------------------------------------------------------- /script/repl.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setLocal EnableDelayedExpansion 3 | 4 | set CLASSPATH=" 5 | for %%j in (".\lib\*.jar") do ( 6 | set CLASSPATH=!CLASSPATH!;%%~fj 7 | ) 8 | set CLASSPATH=!CLASSPATH!" 9 | set CLASSPATH=%CLASSPATH%;src;resources 10 | 11 | set JLINE=jline.ConsoleRunner 12 | 13 | java -Xmx1G -cp %CLASSPATH% %JLINE% clojure.main 14 | 15 | -------------------------------------------------------------------------------- /script/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p releases 4 | lein1 deps 5 | zip -r releases/clojure-koans-`date +"%Y-%m-%d_%H-%M"`.zip \ 6 | . \ 7 | -x "./.idea/*" \ 8 | -x "./.lein-plugins/*" \ 9 | -x "./.git/*" \ 10 | -x "releases/*" 11 | 12 | echo 13 | echo "Don't forget to upload the zipfile (somewhere...)" 14 | echo `ls -t releases/clojure-koans-*.zip | head -n1` 15 | echo "git push" 16 | echo "git push --tags" 17 | echo 18 | 19 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject clojure-koans "0.5.1-SNAPSHOT" 2 | :description "The Clojure koans." 3 | :dependencies [[org.clojure/clojure "1.8.0"] 4 | [koan-engine "0.2.4"]] 5 | :dev-dependencies [[lein-koan "0.1.4"]] 6 | :profiles {:dev {:dependencies [[lein-koan "0.1.4"]]}} 7 | :repl-options {:init-ns koan-engine.runner 8 | :init ^:displace (do (use '[koan-engine.core]))} 9 | :plugins [[lein-koan "0.1.4"]] 10 | :main koan-engine.runner/exec) 11 | -------------------------------------------------------------------------------- /ideaboard.txt: -------------------------------------------------------------------------------- 1 | Concepts / Language Features 2 | ===== 3 | 4 | new record syntax 5 | Agents 6 | Vars 7 | state identity lifetime 8 | immutability / side effects 9 | type hints 10 | Pre and Post conditions of functions 11 | ex-info/ex-data 12 | reducers (?) 13 | transducers (?) - maybe just some basic ones, nothing too crazy 14 | 15 | Particular Functions 16 | ===== 17 | fnil - creating_a_function 18 | juxt - creating_a_function 19 | constantly - creating_a_function 20 | flatten 21 | 22 | frequencies 23 | reductions 24 | keep 25 | keep-indexed 26 | map-indexed 27 | partition-all 28 | partition-by 29 | repeatedly 30 | -------------------------------------------------------------------------------- /src/koans/05_sets.clj: -------------------------------------------------------------------------------- 1 | (ns koans.05-sets 2 | (:require [koan-engine.core :refer :all] 3 | [clojure.set :as set])) 4 | 5 | (meditations 6 | "You can create a set by converting another collection" 7 | (= #{3} (set '(3))) 8 | 9 | "Counting them is like counting other collections" 10 | (= 3 (count #{1 2 3})) 11 | 12 | "Remember that a set is a *mathematical* set" 13 | (= #{1 2 3 4 5} (set '(1 1 2 2 3 3 4 4 5 5))) 14 | 15 | "You can ask clojure for the union of two sets" 16 | (= #{1 2 3 4 5} (set/union #{1 2 3 4} #{2 3 5})) 17 | 18 | "And also the intersection" 19 | (= #{2 3} (set/intersection #{1 2 3 4} #{2 3 5})) 20 | 21 | "But don't forget about the difference" 22 | (= #{1 4} (set/difference #{1 2 3 4 5} #{2 3 5}))) 23 | -------------------------------------------------------------------------------- /src/koans/20_java_interop.clj: -------------------------------------------------------------------------------- 1 | (ns koans.20-java-interop 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "You may have done more with Java than you know" 6 | (= java.lang.String (class "warfare")) ; hint: try typing (javadoc "warfare") in the REPL 7 | 8 | "The dot signifies easy and direct Java interoperation" 9 | (= "SELECT * FROM" (.toUpperCase "select * from")) 10 | 11 | "But instance method calls are very different from normal functions" 12 | (= ["SELECT" "FROM" "WHERE"] (map #(.toUpperCase %) ["select" "from" "where"])) 13 | 14 | "Constructing might be harder than breaking" 15 | (= 10 (let [latch (java.util.concurrent.CountDownLatch. 10)] 16 | (.getCount latch))) 17 | 18 | "Static methods are slashing prices!" 19 | (== 1024 (Math/pow 2 10))) 20 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "ubuntu/xenial64" 3 | 4 | config.vm.network "private_network", ip: "192.168.33.33" 5 | 6 | config.vm.provider "virtualbox" do |vb| 7 | vb.memory = "1024" 8 | end 9 | 10 | config.vm.provision "shell", inline: <<-SHELL 11 | set -x 12 | 13 | #apt-get update 14 | #apt-get upgrade -y 15 | apt-get install -y openjdk-8-jdk 16 | SHELL 17 | 18 | config.vm.provision "shell", privileged: false, inline: <<-SHELL 19 | pwd 20 | mkdir bin 21 | echo "PATH=\$PATH:~/bin" >> .bashrc 22 | 23 | cd bin 24 | wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein 25 | chmod +x lein 26 | ./lein 27 | 28 | cd /vagrant 29 | ~/bin/lein deps 30 | SHELL 31 | end 32 | -------------------------------------------------------------------------------- /src/koans/11_lazy_sequences.clj: -------------------------------------------------------------------------------- 1 | (ns koans.11-lazy-sequences 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "There are many ways to generate a sequence" 6 | (= [1 2 3 4] (range 1 5)) 7 | 8 | "The range starts at the beginning by default" 9 | (= [0 1 2 3 4] (range 5)) 10 | 11 | "Only take what you need when the sequence is large" 12 | (= [0 1 2 3 4 5 6 7 8 9] 13 | (take 10 (range 100))) 14 | 15 | "Or limit results by dropping what you don't need" 16 | (= [95 96 97 98 99] 17 | (drop 95 (range 100))) 18 | 19 | "Iteration provides an infinite lazy sequence" 20 | (= '(1 2 4 8 16 32 64 128) (take 8 (iterate (fn [x] (* x 2)) 1))) 21 | 22 | "Repetition is key" 23 | (= [:a :a :a :a :a :a :a :a :a :a] 24 | (repeat 10 :a)) 25 | 26 | "Iteration can be used for repetition" 27 | (= (repeat 100 "hello") 28 | (take 100 (iterate identity "hello")))) 29 | -------------------------------------------------------------------------------- /src/koans/21_partition.clj: -------------------------------------------------------------------------------- 1 | (ns koans.21-partition 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "To split a collection you can use the partition function" 6 | (= '((0 1) (2 3)) (partition 2 (range 4))) 7 | 8 | "But watch out if there are not enough elements to form n sequences" 9 | (= '((:a :b :c)) (partition 3 [:a :b :c :d :e])) 10 | 11 | "You can use partition-all to include any leftovers too" 12 | (= '((0 1 2) (3 4)) (partition-all 3 (range 5))) 13 | 14 | "If you need to, you can start each sequence with an offset" 15 | (= '((0 1 2) (5 6 7) (10 11 12)) (partition 3 5 (range 13))) 16 | 17 | "Consider padding the last sequence with some default values..." 18 | (= '((0 1 2) (3 4 5) (6 :hello)) (partition 3 3 [:hello] (range 7))) 19 | 20 | "... but notice that they will only pad up to the given sequence length" 21 | (= '((0 1 2) (3 4 5) (6 :these :are)) (partition 3 3 [:these :are "my" "words"] (range 7)))) 22 | -------------------------------------------------------------------------------- /src/koans/18_quote.clj: -------------------------------------------------------------------------------- 1 | (ns koans.18-quote 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | 5 | (meditations 6 | "Wrap a quote around a list to suppress evaluation" 7 | (= (quote (1 2 3 4 5)) (list 1 2 3 4 5)) 8 | 9 | "There is a shortcut too!" 10 | (= (quote (1 2 3 4 5)) '(1 2 3 4 5)) 11 | 12 | "You can quote symbols as well as lists... without evaluation!" 13 | (= 'age (let [age 9] (quote age))) 14 | 15 | "You can use a literal list as a data collection without having Clojure try to call a function" 16 | (= (cons 1 (quote (2 3))) (list 1 2 3) (cons 1 [2 3])) 17 | 18 | "The quote affects all of its arguments, not just the top level" 19 | (= (list 1 '(+ 2 3)) '(1 (+ 2 3))) 20 | 21 | "Syntax-quote (`) acts similarly to the normal quote" 22 | (= (list 1 2 3) `(1 2 3) '(1 2 3)) 23 | 24 | "Unquote (~) within a syntax-quoted expression lets you mark specific expressions as requiring evaluation" 25 | (= (list 1 5) `(1 ~(+ 2 3)) '(1 5))) 26 | -------------------------------------------------------------------------------- /src/koans/17_atoms.clj: -------------------------------------------------------------------------------- 1 | (ns koans.17-atoms 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (def atomic-clock (atom 0)) 5 | 6 | (meditations 7 | "Atoms are like refs" 8 | (= 0 @atomic-clock) 9 | 10 | "You can change at the swap meet" 11 | (= 1 (do 12 | (swap! atomic-clock inc) 13 | @atomic-clock)) 14 | 15 | "Keep taxes out of this: swapping requires no transaction" 16 | (= 5 (do 17 | (swap! atomic-clock + 4) 18 | @atomic-clock)) 19 | 20 | "Any number of arguments might happen during a swap" 21 | (= 20 (do 22 | (swap! atomic-clock + 1 2 3 4 5) 23 | @atomic-clock)) 24 | 25 | "Atomic atoms are atomic" 26 | (= 20 (do 27 | (compare-and-set! atomic-clock 100 :fin) 28 | @atomic-clock)) 29 | 30 | "When your expectations are aligned with reality, things proceed that way" 31 | (= :fin (do 32 | (compare-and-set! atomic-clock 20 :fin) 33 | @atomic-clock))) 34 | -------------------------------------------------------------------------------- /src/koans/04_vectors.clj: -------------------------------------------------------------------------------- 1 | (ns koans.04-vectors 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "You can use vectors in clojure as array-like structures" 6 | (= 1 (count [42])) 7 | 8 | "You can create a vector from a list" 9 | (= [1] (vec '(1))) 10 | 11 | "Or from some elements" 12 | (= [nil nil] (vector nil nil)) 13 | 14 | "But you can populate it with any number of elements at once" 15 | (= [1 2] (vec '(1 2))) 16 | 17 | "Conjoining to a vector is different than to a list" 18 | (= [111 222 333] (conj [111 222] 333)) 19 | 20 | "You can get the first element of a vector like so" 21 | (= :peanut (first [:peanut :butter :and :jelly])) 22 | 23 | "And the last in a similar fashion" 24 | (= :jelly (last [:peanut :butter :and :jelly])) 25 | 26 | "Or any index if you wish" 27 | (= :jelly (nth [:peanut :butter :and :jelly] 3)) 28 | 29 | "You can also slice a vector" 30 | (= [:butter :and] (subvec [:peanut :butter :and :jelly] 1 3)) 31 | 32 | "Equality with collections is in terms of values" 33 | (= (list 1 2 3) (vector 1 2 3))) 34 | -------------------------------------------------------------------------------- /src/koans/07_functions.clj: -------------------------------------------------------------------------------- 1 | (ns koans.07-functions 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defn multiply-by-ten [n] 5 | (* 10 n)) 6 | 7 | (defn square [n] (* n n)) 8 | 9 | (meditations 10 | "Calling a function is like giving it a hug with parentheses" 11 | (= 81 (square 9)) 12 | 13 | "Functions are usually defined before they are used" 14 | (= 20 (multiply-by-ten 2)) 15 | 16 | "But they can also be defined inline" 17 | (= 10 ((fn [n] (* 5 n)) 2)) 18 | 19 | "Or using an even shorter syntax" 20 | (= 60 (#(* 15 %) 4)) 21 | 22 | "Even anonymous functions may take multiple arguments" 23 | (= 15 (#(+ %1 %2 %3) 4 5 6)) 24 | 25 | "Arguments can also be skipped" 26 | (= "AACC" (#(str "AA" %2) "bb" "CC")) 27 | 28 | "One function can beget another" 29 | (= 9 (((fn [] +)) 4 5)) 30 | 31 | "Functions can also take other functions as input" 32 | (= 20 ((fn [f] (f 4 5)) 33 | *)) 34 | 35 | "Higher-order functions take function arguments" 36 | (= 25 (#(%1 5) 37 | (fn [n] (* n n)))) 38 | 39 | "But they are often better written using the names of functions" 40 | (= 25 (#(%1 5) square))) 41 | -------------------------------------------------------------------------------- /src/koans/01_equalities.clj: -------------------------------------------------------------------------------- 1 | (ns koans.01-equalities 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "We shall contemplate truth by testing reality, via equality" 6 | (= true true) 7 | 8 | "To understand reality, we must compare our expectations against reality" 9 | (= 2 (+ 1 1)) 10 | 11 | "You can test equality of many things" 12 | (= (+ 3 4) 7 (+ 2 5)) 13 | 14 | "Some things may appear different, but be the same" 15 | (= true (= 2 2/1)) 16 | 17 | "You cannot generally float to heavens of integers" 18 | (= false (= 2 2.0)) 19 | 20 | "But a looser equality is also possible" 21 | (= true (== 2.0 2)) 22 | 23 | "Something is not equal to nothing" 24 | (= true (not (= 1 nil))) 25 | 26 | "Strings, and keywords, and symbols: oh my!" 27 | (= false (= "hello" :hello 'hello)) 28 | 29 | "Make a keyword with your keyboard" 30 | (= :hello (keyword 'hello)) 31 | 32 | "Symbolism is all around us" 33 | (= 'hello (symbol "hello")) 34 | 35 | "What could be equivalent to nothing?" 36 | (= nil nil) 37 | 38 | "When things cannot be equal, they must be different" 39 | (not= :fill-in-the-blank "what ever")) 40 | -------------------------------------------------------------------------------- /src/koans/12_sequence_comprehensions.clj: -------------------------------------------------------------------------------- 1 | (ns koans.12-sequence-comprehensions 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "Sequence comprehensions can bind each element in turn to a symbol" 6 | (= [0 1 2 3 4 5] 7 | (for [x (range 6)] 8 | x)) 9 | 10 | "They can easily emulate mapping" 11 | (= '(0 1 4 9 16 25) 12 | (map (fn [x] (* x x)) 13 | (range 6)) 14 | (for [x (range 6)] 15 | (* x x))) 16 | 17 | "And also filtering" 18 | (= '(1 3 5 7 9) 19 | (filter odd? (range 10)) 20 | (for [x (range 10) :when (odd? x)] 21 | x)) 22 | 23 | "Combinations of these transformations is trivial" 24 | (= '(1 9 25 49 81) 25 | (map (fn [x] (* x x)) 26 | (filter odd? (range 10))) 27 | (for [x (range 10) :when (odd? x)] 28 | (* x x))) 29 | 30 | "More complex transformations simply take multiple binding forms" 31 | (= [[:top :left] [:top :middle] [:top :right] 32 | [:middle :left] [:middle :middle] [:middle :right] 33 | [:bottom :left] [:bottom :middle] [:bottom :right]] 34 | (for [row [:top :middle :bottom] 35 | column [:left :middle :right]] 36 | [row column]))) 37 | -------------------------------------------------------------------------------- /src/koans/13_creating_functions.clj: -------------------------------------------------------------------------------- 1 | (ns koans.13-creating-functions 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defn square [x] (* x x)) 5 | 6 | (meditations 7 | "One may know what they seek by knowing what they do not seek" 8 | (= [true false true] (let [not-a-symbol? (complement symbol?)] 9 | (map not-a-symbol? [:a 'b "c"]))) 10 | 11 | "Praise and 'complement' may help you separate the wheat from the chaff" 12 | (= [:wheat "wheat" 'wheat] 13 | (let [not-nil? (complement nil?)] 14 | (filter not-nil? [nil :wheat nil "wheat" nil 'wheat nil]))) 15 | 16 | "Partial functions allow procrastination" 17 | (= 20 (let [multiply-by-5 (partial * 5)] 18 | (multiply-by-5 4))) 19 | 20 | "Don't forget: first things first" 21 | (= [:a :b :c :d] 22 | (let [ab-adder (partial concat [:a :b])] 23 | (ab-adder [:c :d]))) 24 | 25 | "Functions can join forces as one 'composed' function" 26 | (= 25 (let [inc-and-square (comp square inc)] 27 | (inc-and-square 4))) 28 | 29 | "Have a go on a double dec-er" 30 | (= 8 (let [double-dec (comp dec dec)] 31 | (double-dec 10))) 32 | 33 | "Be careful about the order in which you mix your functions" 34 | (= 99 (let [square-and-dec (comp dec square)] 35 | (square-and-dec 10)))) 36 | -------------------------------------------------------------------------------- /src/koans/09_higher_order_functions.clj: -------------------------------------------------------------------------------- 1 | (ns koans.09-higher-order-functions 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "The map function relates a sequence to another" 6 | (= [4 8 12] (map (fn [x] (* 4 x)) [1 2 3])) 7 | 8 | "You may create that mapping" 9 | (= [1 4 9 16 25] (map (fn [x] (* x x)) [1 2 3 4 5])) 10 | 11 | "Or use the names of existing functions" 12 | (= [false false true false false] (map nil? [:a :b nil :c :d])) 13 | 14 | "A filter can be strong" 15 | (= '() (filter (fn [x] false) '(:anything :goes :here))) 16 | 17 | "Or very weak" 18 | (= '(:anything :goes :here) (filter (fn [x] true) '(:anything :goes :here))) 19 | 20 | "Or somewhere in between" 21 | (= [10 20 30] (filter (fn [x] (< x 35)) [10 20 30 40 50 60 70 80])) 22 | 23 | "Maps and filters may be combined" 24 | (= [10 20 30] (map (fn [x] (* x 10)) (filter (fn [x] (< x 4)) [1 2 3 4 5 6 7 8]))) 25 | 26 | "Reducing can increase the result" 27 | (= 24 (reduce (fn [a b] (* a b)) [1 2 3 4])) 28 | 29 | "You can start somewhere else" 30 | (= 2400 (reduce (fn [a b] (* a b)) 100 [1 2 3 4])) 31 | 32 | "Numbers are not the only things one can reduce" 33 | (= "longest" (reduce (fn [a b] 34 | (if (< (count a) (count b)) b a)) 35 | ["which" "word" "is" "longest"]))) 36 | -------------------------------------------------------------------------------- /src/koans/03_lists.clj: -------------------------------------------------------------------------------- 1 | (ns koans.03-lists 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "Lists can be expressed by function or a quoted form" 6 | (= '(1 2 3 4 5) (list 1 2 3 4 5)) 7 | 8 | "They are Clojure seqs (sequences), so they allow access to the first" 9 | (= 1 (first '(1 2 3 4 5))) 10 | 11 | "As well as the rest" 12 | (= '(2 3 4 5) (rest '(1 2 3 4 5))) 13 | 14 | "Count your blessings" 15 | (= 3 (count '(dracula dooku chocula))) 16 | 17 | "Before they are gone" 18 | (= 0 (count '())) 19 | 20 | "The rest, when nothing is left, is empty" 21 | (= '() (rest '(100))) 22 | 23 | "Construction by adding an element to the front is easy" 24 | (= '(:a :b :c :d :e) (cons :a '(:b :c :d :e))) 25 | 26 | "Conjoining an element to a list isn't hard either" 27 | (= '(:e :a :b :c :d) (conj '(:a :b :c :d) :e)) 28 | 29 | "You can use a list like a stack to get the first element" 30 | (= :a (peek '(:a :b :c :d :e))) 31 | 32 | "Or the others" 33 | (= '(:b :c :d :e) (pop '(:a :b :c :d :e))) 34 | 35 | "But watch out if you try to pop nothing" 36 | (= "No dice!" (try 37 | (pop '()) 38 | (catch IllegalStateException e 39 | "No dice!"))) 40 | 41 | "The rest of nothing isn't so strict" 42 | (= '() (try 43 | (rest '()) 44 | (catch IllegalStateException e 45 | "No dice!")))) 46 | -------------------------------------------------------------------------------- /src/koans/16_refs.clj: -------------------------------------------------------------------------------- 1 | (ns koans.16-refs 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (def the-world (ref "hello")) 5 | (def bizarro-world (ref {})) 6 | 7 | (meditations 8 | "In the beginning, there was a word" 9 | (= "hello" (deref the-world)) 10 | 11 | "You can get the word more succinctly, but it's the same" 12 | (= "hello" @the-world) 13 | 14 | "You can be the change you wish to see in the world." 15 | (= "better" (do 16 | (dosync (ref-set the-world "better")) 17 | @the-world)) 18 | 19 | "Alter where you need not replace" 20 | (= "better!!!" (let [exclamator (fn [x] (str x "!"))] 21 | (dosync 22 | (alter the-world exclamator) 23 | (alter the-world exclamator) 24 | (alter the-world exclamator)) 25 | @the-world)) 26 | 27 | "Don't forget to do your work in a transaction!" 28 | (= 0 (do (dosync (ref-set the-world 0)) 29 | @the-world)) 30 | 31 | "Functions passed to alter may depend on the data in the ref" 32 | (= 20 (do 33 | (dosync (alter the-world (fn [n] (+ n 20)))))) 34 | 35 | "Two worlds are better than one" 36 | (= ["Real Jerry" "Bizarro Jerry"] 37 | (do 38 | (dosync 39 | (ref-set the-world {}) 40 | (alter the-world assoc :jerry "Real Jerry") 41 | (alter bizarro-world assoc :jerry "Bizarro Jerry") 42 | [(:jerry @the-world) (:jerry @bizarro-world)])))) 43 | -------------------------------------------------------------------------------- /src/koans/08_conditionals.clj: -------------------------------------------------------------------------------- 1 | (ns koans.08-conditionals 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defn explain-exercise-velocity [exercise-term] 5 | (case exercise-term 6 | :bicycling "pretty fast" 7 | :jogging "not super fast" 8 | :walking "not fast at all" 9 | "is that even exercise?")) 10 | 11 | (meditations 12 | "You will face many decisions" 13 | (= :a (if (false? (= 4 5)) 14 | :a 15 | :b)) 16 | 17 | "Some of them leave you no alternative" 18 | (= [] (if (> 4 3) 19 | [])) 20 | 21 | "And in such a situation you may have nothing" 22 | (= nil (if (nil? 0) 23 | [:a :b :c])) 24 | 25 | "In others your alternative may be interesting" 26 | (= :glory (if (not (empty? ())) 27 | :doom 28 | :glory)) 29 | 30 | "You may have a multitude of possible paths" 31 | (let [x 5] 32 | (= :your-road (cond (= x 3) :road-not-taken 33 | (= x 4) :another-road-not-taken 34 | :else :your-road))) 35 | 36 | "Or your fate may be sealed" 37 | (= 'doom (if-not (zero? 1) 38 | 'doom 39 | 'more-doom)) 40 | 41 | "In case of emergency, go fast" 42 | (= "pretty fast" 43 | (explain-exercise-velocity :bicycling)) 44 | 45 | "But admit it when you don't know what to do" 46 | (= "is that even exercise?" 47 | (explain-exercise-velocity :watching-tv))) 48 | -------------------------------------------------------------------------------- /src/koans/24_macros.clj: -------------------------------------------------------------------------------- 1 | (ns koans.24-macros 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defmacro hello [x] 5 | (str "Hello, " x)) 6 | 7 | (defmacro infix [form] 8 | (list (second form) (first form) (nth form 2))) 9 | 10 | (defmacro infix-better [form] 11 | `(~(second form) ; Note the syntax-quote (`) and unquote (~) characters! 12 | ~(first form) 13 | ~(nth form 2))) 14 | 15 | (defmacro r-infix [form] 16 | (cond (not (seq? form)) 17 | form 18 | (= 1 (count form)) 19 | `(r-infix ~(first form)) 20 | :else 21 | (let [operator (second form) 22 | first-arg (first form) 23 | others (rest (rest form))] 24 | `(~operator 25 | (r-infix ~first-arg) 26 | (r-infix ~others))))) 27 | 28 | (meditations 29 | "Macros are like functions created at compile time" 30 | (= "Hello, Macros!" (hello "Macros!")) 31 | 32 | "I can haz infix?" 33 | (= 10 (infix (9 + 1))) 34 | 35 | "Remember, these are nothing but code transformations" 36 | (= '(+ 9 1) (macroexpand '(infix (9 + 1)))) 37 | 38 | "You can do better than that - hand crafting FTW!" 39 | (= '(* 10 2) (macroexpand '(infix-better (10 * 2)))) 40 | 41 | "Things don't always work as you would like them to... " 42 | (= '(+ 10 (2 * 3)) (macroexpand '(infix-better ( 10 + (2 * 3))))) 43 | 44 | "Really, you don't understand recursion until you understand recursion" 45 | (= 36 (r-infix (10 + (2 * 3) + (4 * 5))))) 46 | -------------------------------------------------------------------------------- /src/koans/22_group_by.clj: -------------------------------------------------------------------------------- 1 | (ns koans.22-group-by 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defn get-odds-and-evens [coll] 5 | (let [{odds true evens false} (group-by odd? coll)] 6 | [odds evens])) 7 | 8 | (meditations 9 | "To categorize a collection by some function, use group-by." 10 | (= {5 ["hello" "world"], 3 ["foo" "bar"]} (group-by count ["hello" "world" "foo" "bar"])) 11 | 12 | "You can simulate filter + remove in one pass" 13 | (= (get-odds-and-evens [1 2 3 4 5]) 14 | ((juxt filter remove) odd? [1 2 3 4 5]) 15 | [[1 3 5] [2 4]]) 16 | 17 | "You can also group by a primary key" 18 | (= {1 [{:id 1, :name "Bob"} {:id 1, :last-name "Smith"}], 2 [{:id 2, :name "Jennifer"}]} 19 | (group-by :id [{:id 1 :name "Bob"} 20 | {:id 2 :name "Jennifer"} 21 | {:id 1 :last-name "Smith"} ])) 22 | 23 | "But be careful when you group by a non-required key" 24 | (= {"Bob" [{:name "Bob" :id 1}] 25 | "Jennifer" [{:name "Jennifer" :id 2}] 26 | nil [{:last-name "Smith" :id 1}]} 27 | (group-by :name [{:id 1 :name "Bob"} 28 | {:id 2 :name "Jennifer"} 29 | {:id 1 :last-name "Smith"}])) 30 | 31 | "The true power of group-by comes with custom functions" 32 | (= {:naughty-list [{:name "Jimmy", :bad true} {:name "Joe", :bad true}], :nice-list [{:name "Jane", :bad false}]} 33 | (group-by #(if (:bad %) :naughty-list :nice-list) 34 | [{:name "Jimmy" :bad true} 35 | {:name "Jane" :bad false} 36 | {:name "Joe" :bad true}]))) 37 | -------------------------------------------------------------------------------- /src/koans/10_runtime_polymorphism.clj: -------------------------------------------------------------------------------- 1 | (ns koans.10-runtime-polymorphism 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defn hello 5 | ([] "Hello World!") 6 | ([a] (str "Hello, you silly " a ".")) 7 | ([a & more] (str "Hello to this group: " 8 | (apply str 9 | (interpose ", " (cons a more))) 10 | "!"))) 11 | 12 | (defmulti diet (fn [x] (:eater x))) 13 | (defmethod diet :herbivore [a] (str (a :name) " eats veggies.")) 14 | (defmethod diet :carnivore [a] (str (:name a) " eats animals.")) 15 | (defmethod diet :default [a] (str "I don't know what " (:name a) " eats.")) 16 | 17 | (meditations 18 | "Some functions can be used in different ways - with no arguments" 19 | (= "Hello World!" (hello)) 20 | 21 | "With one argument" 22 | (= "Hello, you silly world." (hello "world")) 23 | 24 | "Or with many arguments" 25 | (= "Hello to this group: Peter, Paul, Mary!" 26 | (hello "Peter" "Paul" "Mary")) 27 | 28 | "Multimethods allow more complex dispatching" 29 | (= "Bambi eats veggies." 30 | (diet {:species "deer" :name "Bambi" :age 1 :eater :herbivore})) 31 | 32 | "Animals have different names" 33 | (= "Thumper eats veggies." 34 | (diet {:species "rabbit" :name "Thumper" :age 1 :eater :herbivore})) 35 | 36 | "Different methods are used depending on the dispatch function result" 37 | (= "Simba eats animals." 38 | (diet {:species "lion" :name "Simba" :age 1 :eater :carnivore})) 39 | 40 | "You may use a default method when no others match" 41 | (= "I don't know what Rich Hickey eats." 42 | (diet {:name "Rich Hickey"}))) 43 | -------------------------------------------------------------------------------- /src/koans/19_datatypes.clj: -------------------------------------------------------------------------------- 1 | (ns koans.19-datatypes 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defrecord Nobel [prize]) 5 | (deftype Pulitzer [prize]) 6 | 7 | (defprotocol Award 8 | (present [this recipient])) 9 | 10 | (defrecord Oscar [category] 11 | Award 12 | (present [this recipient] 13 | (print (str "Congratulations on your " 14 | (:category this) " Oscar, " 15 | recipient 16 | "!")))) 17 | 18 | (deftype Razzie [category] 19 | Award 20 | (present [this recipient] 21 | (print (str "You're really the " 22 | (.category this) ", " 23 | recipient 24 | "... sorry.")))) 25 | 26 | (meditations 27 | "Holding records is meaningful only when the record is worthy of you" 28 | (= "peace" (.prize (Nobel. "peace"))) 29 | 30 | "Types are quite similar" 31 | (= "literature" (.prize (Pulitzer. "literature"))) 32 | 33 | "Records may be treated like maps" 34 | (= "physics" (:prize (Nobel. "physics"))) 35 | 36 | "While types may not" 37 | (= nil (:prize (Pulitzer. "poetry"))) 38 | 39 | "Further study reveals why" 40 | (= [true false] 41 | (map map? [(Nobel. "chemistry") 42 | (Pulitzer. "music")])) 43 | 44 | "Either sort of datatype can define methods in a protocol" 45 | (= "Congratulations on your Best Picture Oscar, Evil Alien Conquerors!" 46 | (with-out-str (present (Oscar. "Best Picture") "Evil Alien Conquerors"))) 47 | 48 | "Surely we can implement our own by now" 49 | (= "You're really the Worst Picture, Final Destination 5... sorry." 50 | (with-out-str (present (Razzie. "Worst Picture") "Final Destination 5")))) 51 | -------------------------------------------------------------------------------- /src/koans/14_recursion.clj: -------------------------------------------------------------------------------- 1 | (ns koans.14-recursion 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (defn is-even? [n] 5 | (if (= n 0) 6 | true 7 | (not (is-even? (dec n))))) 8 | 9 | (defn is-even-bigint? [n] 10 | (loop [n n 11 | acc true] 12 | (if (= n 0) 13 | acc 14 | (recur (dec n) (not acc))))) 15 | 16 | (defn recursive-reverse [coll] 17 | (loop [coll (list* coll) 18 | acc (list)] 19 | (if (empty? coll) 20 | acc 21 | (recur (rest coll) (cons (first coll) acc))))) 22 | 23 | (defn factorial [n] 24 | (loop [n n 25 | acc 1] 26 | (if (= n 0) 27 | acc 28 | (recur (dec n) (* acc n))))) 29 | 30 | (meditations 31 | "Recursion ends with a base case" 32 | (= true (is-even? 0)) 33 | 34 | "And starts by moving toward that base case" 35 | (= false (is-even? 1)) 36 | 37 | "Having too many stack frames requires explicit tail calls with recur" 38 | (= false (is-even-bigint? 100003N)) 39 | 40 | "Reversing directions is easy when you have not gone far" 41 | (= '(1) (recursive-reverse [1])) 42 | 43 | "Yet it becomes more difficult the more steps you take" 44 | (= '(6 5 4 3 2) (recursive-reverse [2 3 4 5 6])) 45 | 46 | "Simple things may appear simple." 47 | (= 1 (factorial 1)) 48 | 49 | "They may require other simple steps." 50 | (= 2 (factorial 2)) 51 | 52 | "Sometimes a slightly bigger step is necessary" 53 | (= 6 (factorial 3)) 54 | 55 | "And eventually you must think harder" 56 | (= 24 (factorial 4)) 57 | 58 | "You can even deal with very large numbers" 59 | (< 1000000000000000000000000N (factorial 1000N)) 60 | 61 | "But what happens when the machine limits you?" 62 | (< 1000000000000000000000000N (factorial 100003N))) 63 | -------------------------------------------------------------------------------- /src/koans/23_meta.clj: -------------------------------------------------------------------------------- 1 | (ns koans.23-meta 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (def giants 5 | (with-meta 'Giants 6 | {:league "National League"})) 7 | 8 | (meditations 9 | "Some objects can be tagged using the with-meta function" 10 | (= {:league "National League"} (meta giants)) 11 | 12 | "Or more succinctly with a reader macro" 13 | (= {:division "West"} (meta '^{:division "West"} Giants)) 14 | 15 | "While others can't" 16 | (= "This doesn't implement the IObj interface" (try 17 | (with-meta 18 | 2 19 | {:prime true}) 20 | (catch ClassCastException e 21 | "This doesn't implement the IObj interface"))) 22 | 23 | "Notice when metadata carries over" 24 | (= {:foo :bar} (meta (merge '^{:foo :bar} {:a 1 :b 2} 25 | {:b 3 :c 4}))) 26 | 27 | "And when it doesn't" 28 | (= nil (meta (merge {:a 1 :b 2} 29 | '^{:foo :bar} {:b 3 :c 4}))) 30 | 31 | "Metadata can be used as a type hint to avoid reflection during runtime" 32 | (= \C (#(.charAt ^String % 0) "Cast me")) 33 | 34 | "You can directly update an object's metadata" 35 | (= 8 (let [giants 36 | (with-meta 37 | 'Giants 38 | {:world-series-titles (atom 7)})] 39 | (swap! (:world-series-titles (meta giants)) inc) 40 | @(:world-series-titles (meta giants)))) 41 | 42 | "You can also create a new object from another object with metadata" 43 | (= {:league "National League" :park "AT&T Park"} 44 | (meta (vary-meta giants 45 | assoc :park "AT&T Park"))) 46 | 47 | "But it won't affect behavior like equality" 48 | (= 'Giants (vary-meta giants dissoc :league)) 49 | 50 | "Or the object's printed representation" 51 | (= "Giants" (pr-str (vary-meta giants dissoc :league)))) 52 | -------------------------------------------------------------------------------- /src/koans/15_destructuring.clj: -------------------------------------------------------------------------------- 1 | (ns koans.15-destructuring 2 | (:require [koan-engine.core :refer :all] 3 | [clojure.string :as string])) 4 | 5 | (def test-address 6 | {:street-address "123 Test Lane" 7 | :city "Testerville" 8 | :state "TX"}) 9 | 10 | (meditations 11 | "Destructuring is an arbiter: it breaks up arguments" 12 | (= ":bar:foo" ((fn [[a b]] (str b a)) 13 | [:foo :bar])) 14 | 15 | "Whether in function definitions" 16 | (= (str "An Oxford comma list of apples, " 17 | "oranges, " 18 | "and pears.") 19 | ((fn [[a b c]] (str "An Oxford comma list of " a ", " 20 | b ", " 21 | "and " c ".")) 22 | ["apples" "oranges" "pears"])) 23 | 24 | "Or in let expressions" 25 | (= "Rich Hickey aka The Clojurer aka Go Time aka Lambda Guru" 26 | (let [[first-name last-name & aliases] 27 | (list "Rich" "Hickey" "The Clojurer" "Go Time" "Lambda Guru")] 28 | (str first-name " " last-name " aka " (string/join " aka " aliases)))) 29 | 30 | "You can regain the full argument if you like arguing" 31 | (= {:original-parts ["Stephen" "Hawking"] :named-parts {:first "Stephen" :last "Hawking"}} 32 | (let [[first-name last-name :as full-name] ["Stephen" "Hawking"]] 33 | {:original-parts full-name :named-parts {:first first-name :last last-name}})) 34 | 35 | "Break up maps by key" 36 | (= "123 Test Lane, Testerville, TX" 37 | (let [{street-address :street-address, city :city, state :state} test-address] 38 | (str street-address ", " city ", " state))) 39 | 40 | "Or more succinctly" 41 | (= "123 Test Lane, Testerville, TX" 42 | (let [{:keys [street-address city state]} test-address] 43 | (str street-address ", " city ", " state))) 44 | 45 | "All together now!" 46 | (= "Test Testerson, 123 Test Lane, Testerville, TX" 47 | ((fn [[a b] {:keys [street-address city state]}] 48 | (str a " " b ", " street-address ", " city ", " state)) 49 | ["Test" "Testerson"] test-address))) 50 | -------------------------------------------------------------------------------- /src/koans/06_maps.clj: -------------------------------------------------------------------------------- 1 | (ns koans.06-maps 2 | (:require [koan-engine.core :refer :all])) 3 | 4 | (meditations 5 | "Don't get lost when creating a map" 6 | (= {:a 1 :b 2} (hash-map :a 1 :b 2)) 7 | 8 | "A value must be supplied for each key" 9 | (= {:a 1} (hash-map :a 1)) 10 | 11 | "The size is the number of entries" 12 | (= 2 (count {:a 1 :b 2})) 13 | 14 | "You can look up the value for a given key" 15 | (= 2 (get {:a 1 :b 2} :b)) 16 | 17 | "Maps can be used as functions to do lookups" 18 | (= 1 ({:a 1 :b 2} :a)) 19 | 20 | "And so can keywords" 21 | (= 1 (:a {:a 1 :b 2})) 22 | 23 | "But map keys need not be keywords" 24 | (= "Sochi" ({2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"} 2014)) 25 | 26 | "You may not be able to find an entry for a key" 27 | (= nil (get {:a 1 :b 2} :c)) 28 | 29 | "But you can provide your own default" 30 | (= :key-not-found (get {:a 1 :b 2} :c :key-not-found)) 31 | 32 | "You can find out if a key is present" 33 | (= true (contains? {:a nil :b nil} :b)) 34 | 35 | "Or if it is missing" 36 | (= false (contains? {:a nil :b nil} :c)) 37 | 38 | "Maps are immutable, but you can create a new and improved version" 39 | (= {1 "January" 2 "February"} (assoc {1 "January"} 2 "February")) 40 | 41 | "You can also create a new version with an entry removed" 42 | (= {1 "January"} (dissoc {1 "January" 2 "February"} 2)) 43 | 44 | "Create a new map by merging" 45 | (= {:a 1 :b 2 :c 3} (merge {:a 1 :b 2} {:c 3})) 46 | 47 | "Specify how to handle entries with same keys when merging" 48 | (= {:a 1 :b 2 :c 3} (merge-with + {:a 1 :b 1} {:b 1 :c 3})) 49 | 50 | "Often you will need to get the keys, but the order is undependable" 51 | (= (list 2010 2014 2018) 52 | (sort (keys { 2014 "Sochi" 2018 "PyeongChang" 2010 "Vancouver"}))) 53 | 54 | "You can get the values in a similar way" 55 | (= (list "PyeongChang" "Sochi" "Vancouver") 56 | (sort (vals {2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"}))) 57 | 58 | "You can even iterate over the map entries as a seq" 59 | (= {:a 2 :b 3} 60 | (into {} 61 | (map 62 | (fn [[k v]] [k (inc v)]) 63 | {:a 1 :b 2})))) 64 | -------------------------------------------------------------------------------- /src/koans/02_strings.clj: -------------------------------------------------------------------------------- 1 | (ns koans.02-strings 2 | (:require [koan-engine.core :refer :all] 3 | [clojure.string :as string])) 4 | 5 | (meditations 6 | "A string is nothing more than text surrounded by double quotes" 7 | (= "hello" "hello") 8 | 9 | "But double quotes are just magic on top of something deeper" 10 | (= "world" (str 'world)) 11 | 12 | "You can do more than create strings, you can put them together" 13 | (= "Cool right?" (str "Cool " 'right?)) 14 | 15 | "You can even get certain characters" 16 | (= \C (get "Characters" 0)) 17 | 18 | "Or even count the characters" 19 | (= 11 (count "Hello World")) 20 | 21 | "But strings and characters are not the same" 22 | (= false (= \c "c")) 23 | 24 | "What if you only wanted to get part of a string?" 25 | (= "World" (subs "Hello World" 6 11)) 26 | 27 | "How about joining together elements in a list?" 28 | (= "123" (string/join '(1 2 3))) 29 | 30 | "What if you wanted to separate them out?" 31 | (= "1, 2, 3" (string/join ", " '(1 2 3))) 32 | 33 | "Maybe you want to separate out all your lines" 34 | (= ["1" "2" "3"] (string/split-lines "1\n2\n3")) 35 | 36 | "You may want to make sure your words are backwards" 37 | (= "olleh" (string/reverse "hello")) 38 | 39 | "Maybe you want to find the index of the first occurrence of a substring" 40 | (= 0 (string/index-of "hello world" "h")) 41 | 42 | "Or maybe the last index of the same" 43 | (= 13 (string/last-index-of "hello world, hello" "hello")) 44 | 45 | "But when something doesn't exist, nothing is found" 46 | (= nil (string/index-of "hello world" "bob")) 47 | 48 | "Sometimes you don't want whitespace cluttering the front and back" 49 | (= "hello world" (string/trim " \nhello world \t \n")) 50 | 51 | "You can check if something is a char" 52 | (= true (char? \c)) 53 | 54 | "But it may not be" 55 | (= false (char? "a")) 56 | 57 | "But chars aren't strings" 58 | (= false (string? \b)) 59 | 60 | "Strings are strings" 61 | (= true (string? "a")) 62 | 63 | "Some strings may be blank" 64 | (= true (string/blank? "")) 65 | 66 | "Even if at first glance they aren't" 67 | (= true (string/blank? " \n \t ")) 68 | 69 | "However, most strings aren't blank" 70 | (= false (string/blank? "hello?\nare you out there?"))) 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | bin 3 | releases 4 | *.jar 5 | 6 | # Created by https://www.gitignore.io/api/clojure,osx,linux,windows,leiningen,intellij,eclipse,sublimetext 7 | 8 | ### Clojure ### 9 | pom.xml 10 | pom.xml.asc 11 | *jar 12 | /lib/ 13 | /classes/ 14 | /target/ 15 | /checkouts/ 16 | .lein-deps-sum 17 | .lein-repl-history 18 | .lein-plugins/ 19 | .lein-failures 20 | .nrepl-port 21 | 22 | 23 | ### OSX ### 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | 28 | # Icon must end with two \r 29 | Icon 30 | 31 | 32 | # Thumbnails 33 | ._* 34 | 35 | # Files that might appear in the root of a volume 36 | .DocumentRevisions-V100 37 | .fseventsd 38 | .Spotlight-V100 39 | .TemporaryItems 40 | .Trashes 41 | .VolumeIcon.icns 42 | 43 | # Directories potentially created on remote AFP share 44 | .AppleDB 45 | .AppleDesktop 46 | Network Trash Folder 47 | Temporary Items 48 | .apdisk 49 | 50 | 51 | ### Linux ### 52 | *~ 53 | 54 | # KDE directory preferences 55 | .directory 56 | 57 | # Linux trash folder which might appear on any partition or disk 58 | .Trash-* 59 | 60 | 61 | ### Windows ### 62 | # Windows image file caches 63 | Thumbs.db 64 | ehthumbs.db 65 | 66 | # Folder config file 67 | Desktop.ini 68 | 69 | # Recycle Bin used on file shares 70 | $RECYCLE.BIN/ 71 | 72 | # Windows Installer files 73 | *.cab 74 | *.msi 75 | *.msm 76 | *.msp 77 | 78 | # Windows shortcuts 79 | *.lnk 80 | 81 | 82 | ### Leiningen ### 83 | pom.xml 84 | pom.xml.asc 85 | *jar 86 | /lib/ 87 | /classes/ 88 | /target/ 89 | /checkouts/ 90 | .lein-deps-sum 91 | .lein-repl-history 92 | .lein-plugins/ 93 | .lein-failures 94 | .nrepl-port 95 | 96 | 97 | ### Intellij ### 98 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 99 | 100 | *.iml 101 | 102 | ## Directory-based project format: 103 | .idea/ 104 | # if you remove the above rule, at least ignore the following: 105 | 106 | # User-specific stuff: 107 | # .idea/workspace.xml 108 | # .idea/tasks.xml 109 | # .idea/dictionaries 110 | 111 | # Sensitive or high-churn files: 112 | # .idea/dataSources.ids 113 | # .idea/dataSources.xml 114 | # .idea/sqlDataSources.xml 115 | # .idea/dynamic.xml 116 | # .idea/uiDesigner.xml 117 | 118 | # Gradle: 119 | # .idea/gradle.xml 120 | # .idea/libraries 121 | 122 | # Mongo Explorer plugin: 123 | # .idea/mongoSettings.xml 124 | 125 | ## File-based project format: 126 | *.ipr 127 | *.iws 128 | 129 | ## Plugin-specific files: 130 | 131 | # IntelliJ 132 | /out/ 133 | 134 | # mpeltonen/sbt-idea plugin 135 | .idea_modules/ 136 | 137 | # JIRA plugin 138 | atlassian-ide-plugin.xml 139 | 140 | # Crashlytics plugin (for Android Studio and IntelliJ) 141 | com_crashlytics_export_strings.xml 142 | crashlytics.properties 143 | crashlytics-build.properties 144 | 145 | 146 | ### Eclipse ### 147 | *.pydevproject 148 | .metadata 149 | .gradle 150 | bin/ 151 | tmp/ 152 | *.tmp 153 | *.bak 154 | *.swp 155 | *~.nib 156 | local.properties 157 | .settings/ 158 | .loadpath 159 | 160 | # Eclipse Core 161 | .project 162 | 163 | # External tool builders 164 | .externalToolBuilders/ 165 | 166 | # Locally stored "Eclipse launch configurations" 167 | *.launch 168 | 169 | # CDT-specific 170 | .cproject 171 | 172 | # JDT-specific (Eclipse Java Development Tools) 173 | .classpath 174 | 175 | # Java annotation processor (APT) 176 | .factorypath 177 | 178 | # PDT-specific 179 | .buildpath 180 | 181 | # sbteclipse plugin 182 | .target 183 | 184 | # TeXlipse plugin 185 | .texlipse 186 | 187 | 188 | ### SublimeText ### 189 | # cache files for sublime text 190 | *.tmlanguage.cache 191 | *.tmPreferences.cache 192 | *.stTheme.cache 193 | 194 | # workspace files are user-specific 195 | *.sublime-workspace 196 | 197 | # project files should be checked into the repository, unless a significant 198 | # proportion of contributors will probably not be using SublimeText 199 | # *.sublime-project 200 | 201 | # sftp configuration file 202 | sftp-config.json 203 | 204 | # Vagrant 205 | .vagrant/ 206 | ubuntu-xenial-16.04-cloudimg-console.log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clojure Koans 2 | 3 | The Clojure Koans are a fun way to get started with Clojure - no experience 4 | assumed or required. Follow the instructions below to start making tests pass! 5 | 6 | 7 | ## Getting Started 8 | 9 | I recommend starting from a cloned or forked repo. This way you'll be able to 10 | track your progress in Git. You might want to create your own branch - that way 11 | if you pull back the latest koans from master, it'll be a bit easier to manage 12 | the inevitable conflicts if we make changes to exercises you've already 13 | completed. 14 | 15 | You have a few options for installation: 16 | 17 | - Install the dependencies for the koans (such as Clojure) on your machine 18 | - Use Vagrant and the configuration in this repository 19 | - Use Docker 20 | 21 | Instructions for each option are below! 22 | 23 | 24 | ### Installation on Your Machine 25 | 26 | The only things you'll need to run the Clojure Koans are: 27 | 28 | - JDK (I suggest version 8, but anything 6 or above should work fine) 29 | - [Leiningen](http://github.com/technomancy/leiningen), a build tool for Clojure 30 | 31 | Once you've cloned this repo and installed the dependencies, you can run: 32 | 33 | ``` 34 | lein repl 35 | ``` 36 | 37 | to make sure all the dependencies get downloaded properly (and then `(exit)` 38 | when you want to quit). See below for details on the REPL. 39 | 40 | 41 | ### Installation with Vagrant 42 | 43 | Make sure you have [Vagrant](https://www.vagrantup.com/) and 44 | [VirtualBox](https://www.virtualbox.org) installed. 45 | In the root directory of the project, execute: 46 | 47 | ``` 48 | vagrant up 49 | vagrant ssh 50 | cd /vagrant 51 | lein koan run 52 | ``` 53 | 54 | 55 | ### Installation with Docker 56 | 57 | Once you've got [Docker](https://www.docker.com/) installed, you're basically 58 | all set. You can run these commands to get started: 59 | 60 | To run koans: 61 | 62 | ``` 63 | docker run --rm -it -v $(pwd):/app -w /app clojure lein koan run 64 | ``` 65 | 66 | To start up a REPL: 67 | 68 | ``` 69 | docker run --rm -it -v $(pwd):/app -w /app clojure lein repl 70 | ``` 71 | 72 | 73 | ## Running the Koans 74 | 75 | Run the koans via: 76 | 77 | `lein koan run` 78 | 79 | If want to run directly from a REPL, once you are inside the `lein repl` prompt you can run the koans with 80 | 81 | `(exec "run")` 82 | 83 | Either way, it's an auto-runner, so as you save your files with the correct 84 | answers, it will advance you to the next koan or file (conveniently, all files 85 | are prefixed with the sequence that you should follow). 86 | 87 | You'll see something like this: 88 | 89 | Now meditate on /home/colin/Projects/clojure-koans/src/koans/01_equalities.clj:3 90 | --------------------- 91 | Assertion failed! 92 | We shall contemplate truth by testing reality, via equality. 93 | (= __ true) 94 | 95 | The output is telling you that you have a failing test in the file named 96 | `01_equalities.clj`, on line 3. So you need to open that file up and make 97 | it pass! You'll always be filling in the blanks to make tests pass. 98 | Sometimes there could be several correct answers (or even an infinite number): 99 | any of them will work in these cases. Some tests will pass even if you replace 100 | the blanks with whitespace (or nothing) instead of the expected answer. Make sure 101 | you give one correct expression to replace each blank. 102 | 103 | The koans differ from normal TDD in that the tests are already written for you, 104 | so you'll have to pay close attention to the failure messages, because up until 105 | the very end, making a test pass means that the next failure message comes 106 | up. 107 | 108 | While it might be easy (especially at first) to fill in the blanks making 109 | things pass, you should work thoughtfully, making sure you understand why the 110 | answer is what it is. Enjoy your path to Clojure enlightenment! 111 | 112 | 113 | ## Trying more things out 114 | 115 | It's very useful to try things out in a REPL (Read-Evaluate-Print Loop) 116 | whenever you get stuck or curious. Run: 117 | 118 | ``` 119 | lein repl 120 | ``` 121 | 122 | and you'll be able to type expressions in, and see what output they produce. 123 | 124 | Here are some interesting commands you might try, once you're in a running REPL: 125 | 126 | ```clojure 127 | (find-doc "vec") 128 | (find-doc #"vec$") 129 | (doc vec) 130 | ``` 131 | 132 | And if those still don't make sense: 133 | 134 | ```clojure 135 | (doc doc) 136 | (doc find-doc) 137 | ``` 138 | 139 | will show you what those commands mean. 140 | 141 | You can exit the REPL with `CTRL-d`, `(exit)`, or `(quit)`. 142 | 143 | 144 | ## Contributing 145 | 146 | Patches are encouraged! Make sure the answer sheet still passes 147 | (`lein koan test`), and send a pull request. 148 | 149 | The file ideaboard.txt has lots of good ideas for new koans to start, or things 150 | to add to existing koans. So write some fun exercises, add your answers to 151 | `resources/koans.clj`, and we'll get them in there! 152 | 153 | Please follow the guidelines in 154 | http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html for 155 | commmit messages, and put your code in a feature branch (not master) before 156 | making the pull request. This makes patches easier to review. 157 | 158 | Feel free to contact me (Colin Jones / trptcolin) on Github or elsewhere if you 159 | have any questions or want more direction before you start pitching in. 160 | 161 | 162 | ## Contributors 163 | 164 | https://github.com/functional-koans/clojure-koans/contributors 165 | 166 | 167 | ## Credits 168 | 169 | These exercises were started by [Aaron Bedra](http://github.com/abedra) of 170 | [Relevance, Inc.](http://github.com/relevance) in early 2010, as a learning 171 | tool for newcomers to functional programming. Aaron's macro-fu makes these 172 | koans clear and fun to use and improve upon, and without Relevance's 173 | initiative, this project would not exist. 174 | 175 | Using the [koans](http://en.wikipedia.org/wiki/koan) metaphor as a tool for 176 | learning a programming language started with the 177 | [Ruby Koans](http://rubykoans.com) by [EdgeCase](http://github.com/edgecase). 178 | 179 | 180 | ## License 181 | 182 | The use and distribution terms for this software are covered by the 183 | Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 184 | which can be found in the file epl-v10.html at the root of this distribution. 185 | By using this software in any fashion, you are agreeing to be bound by 186 | the terms of this license. 187 | -------------------------------------------------------------------------------- /resources/koans.clj: -------------------------------------------------------------------------------- 1 | [["01_equalities" {"__" [true 2 | 2 3 | 5 4 | true 5 | false 6 | true 7 | true 8 | false 9 | "hello" 10 | "hello" 11 | nil 12 | 3]}] 13 | 14 | ["02_strings" {"__" ["hello" 15 | "world" 16 | "Cool " 17 | "right?" 18 | 0 19 | 11 20 | false 21 | 6 11 22 | "123" 23 | ", " 24 | "1" "2" "3" 25 | "olleh" 26 | "hello" 27 | 13 28 | nil 29 | "hello world" 30 | true 31 | false 32 | false 33 | "a" 34 | true 35 | true 36 | false]}] 37 | 38 | ["03_lists" {"__" [1 2 3 4 5 39 | 1 40 | [2 3 4 5] 41 | 3 42 | 0 43 | () 44 | [:a :b :c :d :e] 45 | [:e :a :b :c :d] 46 | :a 47 | [:b :c :d :e] 48 | "No dice!" 49 | ()]}] 50 | 51 | ["04_vectors" {"__" [1 52 | [1] 53 | [nil nil] 54 | 2 55 | [111 222 333] 56 | :peanut 57 | :jelly 58 | :jelly 59 | [:butter :and] 60 | 3]}] 61 | 62 | ["05_sets" {"__" [[3] 63 | 3 64 | #{1 2 3 4 5} 65 | #{1 2 3 4 5} 66 | #{2 3} 67 | #{1 4}]}] 68 | 69 | ["06_maps" {"__" [:b 2 70 | 1 71 | 2 72 | 2 73 | 1 74 | 1 75 | "Sochi" 76 | nil 77 | :key-not-found 78 | true 79 | false 80 | "February" 81 | 1 "January" 82 | :c 3 83 | 2 84 | 2010 2014 2018 85 | "PyeongChang" "Sochi" "Vancouver" 86 | 2 3]}] 87 | 88 | ["07_functions" {"__" [81 89 | 20 90 | 10 91 | 60 92 | 15 93 | "AACC"] 94 | "___" [+ 95 | * 96 | (fn [f] (f 5)) 97 | (fn [f] (f 5))]}] 98 | 99 | ["08_conditionals" {"__" [:a 100 | [] 101 | nil 102 | :glory 103 | 4 6 :your-road 104 | 1 105 | :bicycling 106 | "is that even exercise?"]}] 107 | 108 | ["09_higher_order_functions" {"__" [4 8 12 109 | (* x x) 110 | [false false true false false] 111 | () 112 | [:anything :goes :here] 113 | (< x 31) 114 | (* 10 x) (< x 4) 115 | 24 116 | 100 117 | (count a) (count b)]}] 118 | 119 | ["10_runtime_polymorphism" {"__" [(str (:name a) " eats veggies.") 120 | (str (:name a) " eats animals.") 121 | (str "I don't know what " (:name a) " eats.") 122 | "Hello World!" 123 | "Hello, you silly world." 124 | "Hello to this group: Peter, Paul, Mary!" ]}] 125 | 126 | ["11_lazy_sequences" {"__" [[1 2 3 4] 127 | [0 1 2 3 4] 128 | 10 129 | 95 130 | [1 2 4 8 16 32 64 128] 131 | :a] 132 | "___" [(fn [x] x)]}] 133 | 134 | ["12_sequence_comprehensions" {"__" [[0 1 2 3 4 5] 135 | (* x x) 136 | (range 10) 137 | (odd? x) (* x x) 138 | [row column] 139 | ]}] 140 | 141 | ["13_creating_functions" {"__" [true false true 142 | 4 143 | :a :b :c :d 144 | :c :d 145 | 4 146 | 8] 147 | "___" [(complement nil?) 148 | multiply-by-5 149 | (comp dec square)]}] 150 | 151 | ["14_recursion" {"__" [true 152 | acc 153 | (loop [coll coll 154 | acc ()] 155 | (if (seq coll) 156 | (recur (rest coll) (conj acc (first coll))) 157 | acc)) 158 | (loop [n n 159 | acc 1] 160 | (if (zero? n) 161 | acc 162 | (recur (dec n) (* acc n))))] 163 | "___" [not]}] 164 | 165 | ["15_destructuring" {"__" [":bar:foo" 166 | (format (str "An Oxford comma list of %s, " 167 | "%s, " 168 | "and %s.") 169 | a b c) 170 | (apply str 171 | (interpose " " 172 | (apply list 173 | first-name 174 | last-name 175 | (interleave (repeat "aka") aliases)))) 176 | {:original-parts full-name 177 | :named-parts {:first first-name :last last-name}} 178 | (str street-address ", " city ", " state) 179 | city state 180 | (str street-address ", " city ", " state)] 181 | "___" [(fn [[fname lname] 182 | {:keys [street-address city state]}] 183 | (str fname " " lname ", " 184 | street-address ", " city ", " state)) 185 | ]}] 186 | 187 | ["16_refs" {"__" ["hello" 188 | "hello" 189 | "better" 190 | "better!!!" 191 | (dosync (ref-set the-world 0)) 192 | (map :jerry [@the-world @bizarro-world]) 193 | ] 194 | "___" [(fn [x] (+ 20 x))]}] 195 | 196 | ["17_atoms" {"__" [0 197 | 1 198 | (swap! atomic-clock (partial + 4)) 199 | 20 200 | 20 201 | atomic-clock 20 :fin 202 | ]}] 203 | 204 | ["18_quote" {"__" ['(1 2 3 4 5) 205 | (1 2 3 4 5) 206 | 'age 207 | quote 208 | '(+ 2 3) 209 | 1 2 3 210 | 1 5]}] 211 | 212 | ["19_datatypes" {"__" [(print 213 | (str "You're really the " 214 | (.category this) 215 | ", " recipient "... sorry.")) 216 | "peace" 217 | "literature" 218 | "physics" 219 | nil 220 | [true false] 221 | (str "Congratulations on your Best Picture Oscar, " 222 | "Evil Alien Conquerors!")]}] 223 | 224 | ["20_java_interop" {"__" [java.lang.String 225 | "SELECT * FROM" 226 | 10 227 | 1024 228 | ] 229 | "___" [#(.toUpperCase %) 230 | ]}] 231 | 232 | ["21_partition" {"__" [partition 233 | [:a :b :c] 234 | '((0 1 2) (3 4)) 235 | 5 236 | :hello 237 | (6 :these :are) 238 | ]}] 239 | 240 | ["22_group_by" {"__" [odd? 241 | {5 ["hello" "world"] 3 ["foo" "bar"]} 242 | {1 [{:name "Bob" :id 1} 243 | {:last-name "Smith" :id 1}] 244 | 2 [{:name "Jennifer" :id 2}]} 245 | nil 246 | {:naughty-list [{:name "Jimmy" :bad true} 247 | {:name "Joe" :bad true}] 248 | :nice-list [{:name "Jane" :bad false}]}]}] 249 | 250 | ["23_meta" {"__" [{:league "National League"} 251 | {:division "West"} 252 | "This doesn't implement the IObj interface" 253 | {:foo :bar} 254 | nil 255 | \C 256 | inc 257 | :park "AT&T Park" 258 | 'Giants 259 | "Giants"]}] 260 | 261 | ["24_macros" {"__" [~(first form) 262 | ~(nth form 2) 263 | form 264 | (drop 2 form) 265 | "Hello, Macros!" 266 | 10 267 | '(+ 9 1) 268 | '(* 10 2) 269 | '(+ 10 (2 * 3))]}] 270 | 271 | ] 272 | -------------------------------------------------------------------------------- /epl-v10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Eclipse Public License - Version 1.0 8 | 25 | 26 | 27 | 28 | 29 | 30 |

Eclipse Public License - v 1.0

31 | 32 |

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 33 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR 34 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS 35 | AGREEMENT.

36 | 37 |

1. DEFINITIONS

38 | 39 |

"Contribution" means:

40 | 41 |

a) in the case of the initial Contributor, the initial 42 | code and documentation distributed under this Agreement, and

43 |

b) in the case of each subsequent Contributor:

44 |

i) changes to the Program, and

45 |

ii) additions to the Program;

46 |

where such changes and/or additions to the Program 47 | originate from and are distributed by that particular Contributor. A 48 | Contribution 'originates' from a Contributor if it was added to the 49 | Program by such Contributor itself or anyone acting on such 50 | Contributor's behalf. Contributions do not include additions to the 51 | Program which: (i) are separate modules of software distributed in 52 | conjunction with the Program under their own license agreement, and (ii) 53 | are not derivative works of the Program.

54 | 55 |

"Contributor" means any person or entity that distributes 56 | the Program.

57 | 58 |

"Licensed Patents" mean patent claims licensable by a 59 | Contributor which are necessarily infringed by the use or sale of its 60 | Contribution alone or when combined with the Program.

61 | 62 |

"Program" means the Contributions distributed in accordance 63 | with this Agreement.

64 | 65 |

"Recipient" means anyone who receives the Program under 66 | this Agreement, including all Contributors.

67 | 68 |

2. GRANT OF RIGHTS

69 | 70 |

a) Subject to the terms of this Agreement, each 71 | Contributor hereby grants Recipient a non-exclusive, worldwide, 72 | royalty-free copyright license to reproduce, prepare derivative works 73 | of, publicly display, publicly perform, distribute and sublicense the 74 | Contribution of such Contributor, if any, and such derivative works, in 75 | source code and object code form.

76 | 77 |

b) Subject to the terms of this Agreement, each 78 | Contributor hereby grants Recipient a non-exclusive, worldwide, 79 | royalty-free patent license under Licensed Patents to make, use, sell, 80 | offer to sell, import and otherwise transfer the Contribution of such 81 | Contributor, if any, in source code and object code form. This patent 82 | license shall apply to the combination of the Contribution and the 83 | Program if, at the time the Contribution is added by the Contributor, 84 | such addition of the Contribution causes such combination to be covered 85 | by the Licensed Patents. The patent license shall not apply to any other 86 | combinations which include the Contribution. No hardware per se is 87 | licensed hereunder.

88 | 89 |

c) Recipient understands that although each Contributor 90 | grants the licenses to its Contributions set forth herein, no assurances 91 | are provided by any Contributor that the Program does not infringe the 92 | patent or other intellectual property rights of any other entity. Each 93 | Contributor disclaims any liability to Recipient for claims brought by 94 | any other entity based on infringement of intellectual property rights 95 | or otherwise. As a condition to exercising the rights and licenses 96 | granted hereunder, each Recipient hereby assumes sole responsibility to 97 | secure any other intellectual property rights needed, if any. For 98 | example, if a third party patent license is required to allow Recipient 99 | to distribute the Program, it is Recipient's responsibility to acquire 100 | that license before distributing the Program.

101 | 102 |

d) Each Contributor represents that to its knowledge it 103 | has sufficient copyright rights in its Contribution, if any, to grant 104 | the copyright license set forth in this Agreement.

105 | 106 |

3. REQUIREMENTS

107 | 108 |

A Contributor may choose to distribute the Program in object code 109 | form under its own license agreement, provided that:

110 | 111 |

a) it complies with the terms and conditions of this 112 | Agreement; and

113 | 114 |

b) its license agreement:

115 | 116 |

i) effectively disclaims on behalf of all Contributors 117 | all warranties and conditions, express and implied, including warranties 118 | or conditions of title and non-infringement, and implied warranties or 119 | conditions of merchantability and fitness for a particular purpose;

120 | 121 |

ii) effectively excludes on behalf of all Contributors 122 | all liability for damages, including direct, indirect, special, 123 | incidental and consequential damages, such as lost profits;

124 | 125 |

iii) states that any provisions which differ from this 126 | Agreement are offered by that Contributor alone and not by any other 127 | party; and

128 | 129 |

iv) states that source code for the Program is available 130 | from such Contributor, and informs licensees how to obtain it in a 131 | reasonable manner on or through a medium customarily used for software 132 | exchange.

133 | 134 |

When the Program is made available in source code form:

135 | 136 |

a) it must be made available under this Agreement; and

137 | 138 |

b) a copy of this Agreement must be included with each 139 | copy of the Program.

140 | 141 |

Contributors may not remove or alter any copyright notices contained 142 | within the Program.

143 | 144 |

Each Contributor must identify itself as the originator of its 145 | Contribution, if any, in a manner that reasonably allows subsequent 146 | Recipients to identify the originator of the Contribution.

147 | 148 |

4. COMMERCIAL DISTRIBUTION

149 | 150 |

Commercial distributors of software may accept certain 151 | responsibilities with respect to end users, business partners and the 152 | like. While this license is intended to facilitate the commercial use of 153 | the Program, the Contributor who includes the Program in a commercial 154 | product offering should do so in a manner which does not create 155 | potential liability for other Contributors. Therefore, if a Contributor 156 | includes the Program in a commercial product offering, such Contributor 157 | ("Commercial Contributor") hereby agrees to defend and 158 | indemnify every other Contributor ("Indemnified Contributor") 159 | against any losses, damages and costs (collectively "Losses") 160 | arising from claims, lawsuits and other legal actions brought by a third 161 | party against the Indemnified Contributor to the extent caused by the 162 | acts or omissions of such Commercial Contributor in connection with its 163 | distribution of the Program in a commercial product offering. The 164 | obligations in this section do not apply to any claims or Losses 165 | relating to any actual or alleged intellectual property infringement. In 166 | order to qualify, an Indemnified Contributor must: a) promptly notify 167 | the Commercial Contributor in writing of such claim, and b) allow the 168 | Commercial Contributor to control, and cooperate with the Commercial 169 | Contributor in, the defense and any related settlement negotiations. The 170 | Indemnified Contributor may participate in any such claim at its own 171 | expense.

172 | 173 |

For example, a Contributor might include the Program in a commercial 174 | product offering, Product X. That Contributor is then a Commercial 175 | Contributor. If that Commercial Contributor then makes performance 176 | claims, or offers warranties related to Product X, those performance 177 | claims and warranties are such Commercial Contributor's responsibility 178 | alone. Under this section, the Commercial Contributor would have to 179 | defend claims against the other Contributors related to those 180 | performance claims and warranties, and if a court requires any other 181 | Contributor to pay any damages as a result, the Commercial Contributor 182 | must pay those damages.

183 | 184 |

5. NO WARRANTY

185 | 186 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 187 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 188 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 189 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 190 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 191 | responsible for determining the appropriateness of using and 192 | distributing the Program and assumes all risks associated with its 193 | exercise of rights under this Agreement , including but not limited to 194 | the risks and costs of program errors, compliance with applicable laws, 195 | damage to or loss of data, programs or equipment, and unavailability or 196 | interruption of operations.

197 | 198 |

6. DISCLAIMER OF LIABILITY

199 | 200 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 201 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 202 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 203 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 204 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 205 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 206 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 207 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

208 | 209 |

7. GENERAL

210 | 211 |

If any provision of this Agreement is invalid or unenforceable under 212 | applicable law, it shall not affect the validity or enforceability of 213 | the remainder of the terms of this Agreement, and without further action 214 | by the parties hereto, such provision shall be reformed to the minimum 215 | extent necessary to make such provision valid and enforceable.

216 | 217 |

If Recipient institutes patent litigation against any entity 218 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 219 | Program itself (excluding combinations of the Program with other 220 | software or hardware) infringes such Recipient's patent(s), then such 221 | Recipient's rights granted under Section 2(b) shall terminate as of the 222 | date such litigation is filed.

223 | 224 |

All Recipient's rights under this Agreement shall terminate if it 225 | fails to comply with any of the material terms or conditions of this 226 | Agreement and does not cure such failure in a reasonable period of time 227 | after becoming aware of such noncompliance. If all Recipient's rights 228 | under this Agreement terminate, Recipient agrees to cease use and 229 | distribution of the Program as soon as reasonably practicable. However, 230 | Recipient's obligations under this Agreement and any licenses granted by 231 | Recipient relating to the Program shall continue and survive.

232 | 233 |

Everyone is permitted to copy and distribute copies of this 234 | Agreement, but in order to avoid inconsistency the Agreement is 235 | copyrighted and may only be modified in the following manner. The 236 | Agreement Steward reserves the right to publish new versions (including 237 | revisions) of this Agreement from time to time. No one other than the 238 | Agreement Steward has the right to modify this Agreement. The Eclipse 239 | Foundation is the initial Agreement Steward. The Eclipse Foundation may 240 | assign the responsibility to serve as the Agreement Steward to a 241 | suitable separate entity. Each new version of the Agreement will be 242 | given a distinguishing version number. The Program (including 243 | Contributions) may always be distributed subject to the version of the 244 | Agreement under which it was received. In addition, after a new version 245 | of the Agreement is published, Contributor may elect to distribute the 246 | Program (including its Contributions) under the new version. Except as 247 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no 248 | rights or licenses to the intellectual property of any Contributor under 249 | this Agreement, whether expressly, by implication, estoppel or 250 | otherwise. All rights in the Program not expressly granted under this 251 | Agreement are reserved.

252 | 253 |

This Agreement is governed by the laws of the State of New York and 254 | the intellectual property laws of the United States of America. No party 255 | to this Agreement will bring a legal action under this Agreement more 256 | than one year after the cause of action arose. Each party waives its 257 | rights to a jury trial in any resulting litigation.

258 | 259 | 260 | 261 | 262 | --------------------------------------------------------------------------------