Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Божидар

Slide 3

Slide 3 text

Bozhidar

Slide 4

Slide 4 text

Bozho cool

Slide 5

Slide 5 text

Bozo not cool

Slide 6

Slide 6 text

Божo

Slide 7

Slide 7 text

Bug cool

Slide 8

Slide 8 text

Magical Sofia City Sofia, Bulgaria

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

I’m an Emacs fanatic

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

bbatsov

Slide 18

Slide 18 text

Ruby & Rails style guides

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Clojure Style Guide

Slide 23

Slide 23 text

Towards AWESOME Clojure doCUmentation by Bozhidar Batsov

Slide 24

Slide 24 text

Documentation???

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

What about clean and simple code?

Slide 28

Slide 28 text

(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)))))) ([f coll] (lazy-seq (when-let [s (seq coll)] (if (chunked-seq? s) (let [c (chunk-first s) size (int (count c)) b (chunk-buffer size)] (dotimes [i size] (chunk-append b (f (.nth c i)))) (chunk-cons (chunk b) (map f (chunk-rest s)))) (cons (f (first s)) (map f (rest s))))))) ([f c1 c2] (lazy-seq (let [s1 (seq c1) s2 (seq c2)] (when (and s1 s2) (cons (f (first s1) (first s2)) (map f (rest s1) (rest s2))))))) ([f c1 c2 c3] (lazy-seq (let [s1 (seq c1) s2 (seq c2) s3 (seq c3)] (when (and s1 s2 s3) (cons (f (first s1) (first s2) (first s3)) (map f (rest s1) (rest s2) (rest s3))))))) ([f c1 c2 c3 & colls] (let [step (fn step [cs] (lazy-seq (let [ss (map seq cs)] (when (every? identity ss) (cons (map first ss) (step (map rest ss)))))))] (map #(apply f %) (step (conj colls c3 c2 c1))))))

Slide 29

Slide 29 text

"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."

Slide 30

Slide 30 text

Developers hate writing documentation, right?

Slide 31

Slide 31 text

Bad developers hate writing documentation.

Slide 32

Slide 32 text

Programs must be written for people to read, and only incidentally for machines to execute. — Hal Abelson

Slide 33

Slide 33 text

Documentation is a force for good.

Slide 34

Slide 34 text

Developers who write good documentation are heroes.

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Is Clojure’s documentation awesome?

Slide 37

Slide 37 text

No.

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

clojure.org

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

(defmacro when "Evaluates test. If logical true, evaluates body in an implicit do." {:added "1.0"} [test & body] (list 'if test (cons 'do body)))

Slide 44

Slide 44 text

clojuredocs.org

Slide 45

Slide 45 text

clojure-doc.org

Slide 46

Slide 46 text

conj.io

Slide 47

Slide 47 text

Grimoire

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Something’s definitely wrong with Clojure’s documentation…

Slide 50

Slide 50 text

The Problems

Slide 51

Slide 51 text

Clojure’s core team doesn’t care much about documentation

Slide 52

Slide 52 text

It’s hard to contribute simple documentation fixes

Slide 53

Slide 53 text

Relax the rules for documentation improvements

Slide 54

Slide 54 text

•don’t require CA for doc improvements •accept doc improvements via GitHub •have a simplified review process for doc improvements

Slide 55

Slide 55 text

clojure.org already does this!!!

Slide 56

Slide 56 text

(but I guess many people don’t know that)

Slide 57

Slide 57 text

Clojure itself doesn’t

Slide 58

Slide 58 text

Onward to API docs

Slide 59

Slide 59 text

Does Clojure provide us the infrastructure to create awesome API documentation?

Slide 60

Slide 60 text

No.

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

The docstring format is bad

Slide 63

Slide 63 text

•no clear formatting rules •it’s not editor-friendly •it’s hard to make out parameters and references there

Slide 64

Slide 64 text

(defn str "With no args, returns the empty string. With one arg x, returns x.toString(). (str nil) returns the empty string. With more than one arg, returns the concatenation of the str values of the args." {:tag String :added "1.0" :static true} (^String [] "") (^String [^Object x] (if (nil? x) "" (. x (toString)))) (^String [x & ys] ((fn [^StringBuilder sb more] (if more (recur (. sb (append (str (first more)))) (next more)) (str sb))) (new StringBuilder (str x)) ys)))

Slide 65

Slide 65 text

(defn str "With no args, returns the empty string. With one arg x, returns x.toString(). (str nil) returns the empty string. With more than one arg, returns the concatenation of the str values of the args." {:tag String :added "1.0" :static true}

Slide 66

Slide 66 text

Comparison with Emacs Lisp

Slide 67

Slide 67 text

(defun cider-interactive-eval (form &optional callback bounds additional-params) "Evaluate FORM and dispatch the response to CALLBACK. If the code to be evaluated comes from a buffer, it is preferred to use a nil FORM, and specify the code via the BOUNDS argument instead. This function is the main entry point in CIDER's interactive evaluation API. Most other interactive eval functions should rely on this function. If CALLBACK is nil use `cider-interactive-eval-handler'. BOUNDS, if non-nil, is a list of two numbers marking the start and end positions of FORM in its buffer. ADDITIONAL-PARAMS is a plist to be appended to the request message. If `cider-interactive-eval-override' is a function, call it with the same arguments and only proceed with evaluation if it returns nil."

Slide 68

Slide 68 text

(defn str "Converts something to a string. With no args, returns the empty string. With one arg X, returns `x.toString()`. `(str nil)` returns the empty string. With more than one arg, returns the concatenation of the string values of the args." {:tag String :added "1.0" :static true}

Slide 69

Slide 69 text

Namespace documentation

Slide 70

Slide 70 text

(ns ^{:doc "edn reading." :author "Rich Hickey"} clojure.edn (:refer-clojure :exclude [read read-string]))

Slide 71

Slide 71 text

(ns ^{:doc "Clojure String utilities It is poor form to (:use clojure.string). Instead, use require with :as to specify a prefix, e.g. (ns your.namespace.here (:require [clojure.string :as str])) Design notes for clojure.string: 1. Strings are objects (as opposed to sequences). As such, the string being manipulated is the first argument to a function; passing nil will result in a NullPointerException unless documented otherwise. If you want sequence-y behavior instead, use a sequence. 2. Functions are generally not lazy, and call straight to host methods where those are available and efficient. 3. Functions take advantage of String implementation details to write high-performing loop/recurs instead of using higher-order functions. (This is not idiomatic in general-purpose application code.) 4. When a function is documented to accept a string argument, it will take any implementation of the correct *interface* on the host platform. In Java, this is CharSequence, which is more general than String. In ordinary usage you will almost always pass concrete strings. If you are doing something unusual, e.g. passing a mutable implementation of CharSequence, then thread-safety is your responsibility." :author "Stuart Sierra, Stuart Halloway, David Liebke"} clojure.string (:refer-clojure :exclude (replace reverse)) (:import (java.util.regex Pattern Matcher) clojure.lang.LazilyPersistentVector))

Slide 72

Slide 72 text

(ns ^{:author "Stuart Sierra, Stuart Halloway, David Liebke"} "Clojure String utilities It is poor form to (:use clojure.string). Instead, use require with :as to specify a prefix, e.g. (ns your.namespace.here (:require [clojure.string :as str])) Design notes for clojure.string: 1. Strings are objects (as opposed to sequences). As such, the string being manipulated is the first argument to a function; passing nil will result in a NullPointerException unless documented otherwise. If you want sequence-y behavior instead, use a sequence. 2. Functions are generally not lazy, and call straight to host methods where those are available and efficient. 3. Functions take advantage of String implementation details to write high-performing loop/recurs instead of using higher-order functions. (This is not idiomatic in general-purpose application code.) 4. When a function is documented to accept a string argument, it will take any implementation of the correct *interface* on the host platform. In Java, this is CharSequence, which is more general than String. In ordinary usage you will almost always pass concrete strings. If you are doing something unusual, e.g. passing a mutable implementation of CharSequence, then thread-safety is your responsibility." clojure.string (:refer-clojure :exclude (replace reverse)) (:import (java.util.regex Pattern Matcher) clojure.lang.LazilyPersistentVector))

Slide 73

Slide 73 text

Some def macros don’t support docstrings directly

Slide 74

Slide 74 text

(defonce ^:dynamic ^{:private true :doc "A ref to a sorted set of symbols representing loaded libs"} *loaded-libs* (ref (sorted-set)))

Slide 75

Slide 75 text

This shouldn’t be hard to fix, right?

Slide 76

Slide 76 text

No unified convention about documentation-related metadata usage

Slide 77

Slide 77 text

How do you handle deprecations?

Slide 78

Slide 78 text

How do you indicate when something was introduced/ changed?

Slide 79

Slide 79 text

(defn agent-errors "DEPRECATED: Use 'agent-error' instead. Returns a sequence of the exceptions thrown during asynchronous actions of the agent." {:added "1.0" :deprecated "1.2"} [a] (when-let [e (agent-error a)] (list e))) (defn clear-agent-errors "DEPRECATED: Use 'restart-agent' instead. Clears any exceptions thrown during asynchronous actions of the agent, allowing subsequent actions to occur." {:added "1.0" :deprecated "1.2"} [^clojure.lang.Agent a] (restart-agent a (.deref a)))

Slide 80

Slide 80 text

How do you indicate how some macro is supposed to be indented by Clojure editors?

Slide 81

Slide 81 text

What’s the metadata every function should have?

Slide 82

Slide 82 text

And what about each namespace?

Slide 83

Slide 83 text

Common Metadata • version added (e.g. “:added”) • version compatibility broken (e.g. “:changed”) • version when deprecated (e.g. “:deprecated”) • superseded by • related symbols (e.g. “:see also”) • indentation specification

Slide 84

Slide 84 text

Specifying indentation

Slide 85

Slide 85 text

(defmacro with-in-str "[DOCSTRING]" {:style/indent 1} [s & body] (bla bla bla))

Slide 86

Slide 86 text

(with-in-str some-str (something) (quick))

Slide 87

Slide 87 text

(with-in-str some-str (something) (quick))

Slide 88

Slide 88 text

(defmacro do "[DOCSTRING]" {:style/indent 0} [& body] (bla bla bla))

Slide 89

Slide 89 text

(do (something) (quick))

Slide 90

Slide 90 text

http://cider.readthedocs.io/ en/latest/indent_spec/

Slide 91

Slide 91 text

Epilogue

Slide 92

Slide 92 text

–Mahatma Gandhi “You must be the change you wish to see in the world.”

Slide 93

Slide 93 text

–Bozhidar Batsov “You must be the change you wish to see in the code.”

Slide 94

Slide 94 text

Codify the best practices

Slide 95

Slide 95 text

Send documentation patches to Clojure and Clojure projects

Slide 96

Slide 96 text

Share the best practices and your experience with them

Slide 97

Slide 97 text

Let’s make Clojure’s documentation better together!

Slide 98

Slide 98 text

Let’s make Clojure’s documentation awesome together!

Slide 99

Slide 99 text

Felina

Slide 100

Slide 100 text

Epilogue twitter: @bbatsov github: @bbatsov http//batsov.com http://emacsredux.com ClojuTRE Tampere, Finland 02.09.2017