462 Views
October 13, 17
スライド概要
Enjoy REPL-driven development with Clojure! (*> ᴗ •*)ゞ
「楽しく楽にcoolにsmartに」を理想とするprogrammer/philosopher/liberalist/realist。 好きな言語はClojure, Haskell, Python, English, français, русский。 読書、プログラミング、語学、法学、数学が大好き! イルカと海も大好き🐬
Practical REPL-driven Development with Clojure
Self-introduction lagénorhynque /laʒenɔʁɛ k ̃ / カマイルカ (defprofile lagénorhynque :name "Kent OHASHI" :languages [Clojure Haskell Python Scala English français Deutsch русский] :interests [programming language-learning mathematics] :contributing [github.com/japan-clojurians/clojure-site-ja])
Contents 1. Clojure Quick Intro 2. Leiningen 3. Clojure REPL 4. Lisp Editing 5. REPL-driven Development in Practice
Clojure Quick Intro
Clojure Lisp S-expressions, macros, etc. REPL-driven development functional programming language dynamic language JVM language (cf. ClojureScript) ⇒ simple and powerful language
literals type example string "abc" character \a number 1, 2.0, 3N, 4.5M, 6/7, 8r10 boolean true, false nil nil keyword :a, :user/a, ::a, ::x/a symbol 'a, 'user/a, `a, `x/a
type example list '(1 2 3), '(+ 1 2 3) vector [1 2 3] set #{1 2 3} map {:a 1 :b 2}, #:user{:a 1 :b 2}, #::{:a 1 :b 2}, #::x{:a 1 :b 2} function (fn [x] (* x x))
the syntax (op arg1 arg2 ... argn) operator function macro special form
Leiningen
lein repl starts Clojure REPL $ lein repl nREPL server started on port 49482 on host 127.0.0.1 - nrepl://1 27.0.0.1:49482 REPL-y 0.3.7, nREPL 0.2.12 Clojure 1.8.0 Java HotSpot(TM) 64-Bit Server VM 1.8.0_121-b13 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e user=>
lein new generates a new Clojure project $ lein new app clj-demo Generating a project called clj-demo based on the 'app' template generate a project with app template cf. lein-template
$ tree clj-demo/ clj-demo/ ├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc │ └── intro.md ├── project.clj ├── resources ├── src │ └── clj_demo │ └── core.clj └── test └── clj_demo └── core_test.clj
lein run runs -main function $ cd clj-demo/ $ lein run Hello, World!
cf. src/clj_demo/core.clj
(ns clj-demo.core
(:gen-class))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
lein test runs tests $ lein test lein test clj-demo.core-test lein test :only clj-demo.core-test/a-test FAIL in (a-test) (core_test.clj:7) FIXME, I fail. expected: (= 0 1) actual: (not (= 0 1)) Ran 1 tests containing 1 assertions. 1 failures, 0 errors. Tests failed.
cf. test/clj_demo/core_test.clj (ns clj-demo.core-test (:require [clojure.test :refer :all] [clj-demo.core :refer :all])) (deftest a-test (testing "FIXME, I fail." (is (= 0 1))))
lein uberjar creates jar files $ lein uberjar Compiling clj-demo.core Created /Users/k.ohashi/code/clj-demo/target/uberjar/clj-demo-0. 1.0-SNAPSHOT.jar Created /Users/k.ohashi/code/clj-demo/target/uberjar/clj-demo-0. 1.0-SNAPSHOT-standalone.jar $ java -jar target/uberjar/clj-demo-0.1.0-SNAPSHOT-standalone.ja r Hello, World!
Clojure REPL
evaluate expressions clj-demo.core=> (map inc [0 1 2]) (1 2 3) clj-demo.core=> (println (map inc [0 1 2])) (1 2 3) nil
clojure.repl/doc prints documentation clj-demo.core=> (doc map) ------------------------clojure.core/map ([f] [f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls]) Returns a lazy sequence consisting of the result of applying f to the set of first items of each coll, followed by applying f to the set of second items in each coll, until any one of the colls is exhausted. Any remaining items in other colls are ignored. Function f should accept number-of-colls arguments. Returns a transducer when no collection is provided. nil
clojure.repl/source
prints source code
clj-demo.core=> (source map)
(defn map
"Returns a lazy sequence consisting of the result of applying f to
the set of first items of each coll, followed by applying f to the
set of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments. Returns a transducer when
no collection is provided."
{:added "1.0"
:static true}
([f]
(fn [rf]
(fn
([] (rf))
([result] (rf result))
([result input]
(rf result (f input)))
([result input & inputs]
(rf result (apply f input inputs))))))
clojure.core/*1, *2, *3 special vars bound to the most recent values printed clj-demo.core=> 21 21 clj-demo.core=> 2 2 clj-demo.core=> (* *2 *1) 42
clojure.core/*e
special var bound to the most recent exception
caught by the REPL
clj-demo.core=> (+ 1 "2")
ClassCastException java.lang.String cannot be cast to java.lang.Number
clj-demo.core=> *e
#error {
:cause "java.lang.String cannot be cast to java.lang.Number"
:via
[{:type java.lang.ClassCastException
:message "java.lang.String cannot be cast to java.lang.Number"
:at [clojure.lang.Numbers add "Numbers.java" 128]}]
:trace
[[clojure.lang.Numbers add "Numbers.java" 128]
[clojure.lang.Numbers add "Numbers.java" 3640]
[clj_demo.core$eval1328 invokeStatic "form-init3161334546666357405.cl
[clj_demo.core$eval1328 invoke "form-init3161334546666357405.clj" 1]
[clojure.lang.Compiler eval "Compiler.java" 6927]
[clojure.lang.Compiler eval "Compiler.java" 6890]
[clojure.core$eval invokeStatic "core.clj" 3105]
search documentation clojure.repl/find-doc clojure.repl/apropos clojure.java.javadoc/javadoc
Lisp editing
Lisp editing plugins Parinfer ParEdit
Parinfer automatically adjust parentheses when editing indentation (Indent mode), indentation when editing parentheses (Paren mode)
e.g. Atom with Parinfer
(
defn␣my-map␣ [ f␣coll
⌘-<RET> <TAB>
(
when-let␣ [ s␣ ( seq␣coll
⌘-<RET> <TAB>
(
cons␣ ( f␣ ( first␣s
⌘-<RET> <TAB> <TAB>
(
my-map␣f␣ ( rest␣s
<up> <up> ⌘-<left>
(
lazy-seq <RET>
<down> <TAB> <down> <TAB>
ParEdit semi-automatically adjust parentheses and indentation cf. Smartparens
e.g. Emacs with ParEdit
(
defn␣my-map␣ [ f␣coll
)
<RET>
(
when-let␣ [ s␣ ( seq␣coll
) )
<RET> (
cons␣ ( f␣ ( first␣s
) )
<RET>
( my-map␣f␣ ( rest␣s
<C>-<M>-u <C>-<M>-u <C>-<M>-u <C>-<M>-u
<M>-(
lazy-seq <RET>
REPL-driven Development in Practice
Course A: Clojure Koans Course B: 4Clojure
Clojure Koans cf. ClojureScript Koans
Clone the repository $ git clone git://github.com/functional-koans/clojure-koans.git Cloning into 'clojure-koans'... remote: Counting objects: 1590, done. remote: Compressing objects: 100% (7/7), done. remote: Total 1590 (delta 2), reused 4 (delta 1), pack-reused 15 80 Receiving objects: 100% (1590/1590), 269.87 KiB | 651.00 KiB/s, done. Resolving deltas: 100% (755/755), done.
Run koans
$ cd clojure-koans/
$ lein koan run
Starting auto-runner...
Considering /Users/k.ohashi/code/clojure-koans/src/koans/01_equa
lities.clj...
Now meditate upon /Users/k.ohashi/code/clojure-koans/src/koans/0
1_equalities.clj
--------------------Assertion failed!
clojure.lang.ExceptionInfo: We shall contemplate truth by testin
g reality, via equality
(= __ true) {:line 6}, compiling:(/Users/k.ohashi/code/clojure-k
oans/src/koans/01_equalities.clj:4:1)
Open a source le with your favourite editor $ emacs src/koans/01_equalities.clj
Start Clojure REPL
Evaluate S-expressions
Send S-expressions to REPL
Save changes and check the meditation result
...
Now meditate upon /Users/k.ohashi/code/clojure-koans/src/koans/0
1_equalities.clj
--------------------Assertion failed!
clojure.lang.ExceptionInfo: You can test equality of many things
(= (+ 3 4) 7 (+ 2 __)) {:line 12}, compiling:(/Users/k.ohashi/co
de/clojure-koans/src/koans/01_equalities.clj:4:1)
4Clojure
Open a new buffer with your favourite editor $ emacs clj-demo/src/clj_demo/4clojure/problem22.clj
Start Clojure REPL
e.g. Problem #22 Count a Sequence Write a function which returns the total number of elements in a sequence. (= (= (= (= (= (__ (__ (__ (__ (__ '(1 2 3 3 1)) 5) "Hello World") 11) [[1 2] [3 4] [5 6]]) 3) '(13)) 1) '(:a :b :c)) 3) Special Restrictions: count
Write a function
Evaluate S-expressions
Send S-expressions to REPL
Run your solution on the problem page (fn my-count [coll] (reduce (fn [n _] (inc n)) 0 coll))
Enjoy REPL-driven development!
Further Reading
Clojure & ClojureScript Clojure ClojureScript build tool Leiningen
editor plugins/settings Clojure develompent Emacs CIDER Clojure 初⼼者のための Emacs 設定作りました 新: Emacs を使うモダンな Clojure 開発環境 cf. Spacemacs Clojure layer Clojure development with Spacemacs & Cider
IntelliJ IDEA Cursive IntelliJ IDEA と Cursive で始める — Clojure の⽇ 本語ガイド Atom Atom Clojure Setup ClojureやりたいけどEmacsは厳しい⼈のための proto-repl⼊⾨
Vim fireplace.vim Visual Studio Code clojureVSCode Sublime Text SublimeClojureSetup
Lisp editing Parinfer ParEdit ParEdit チュートリアル Paredit Cheatsheet The Animated Guide to Paredit Lispってどう書くの? - Qiita Smartparens
REPL-driven development Re:REPL-Driven Development REPL Driven Development REPL Driven Development and Testing in Clojure