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

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

I’m an Emacs fanatic

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

bbatsov

Slide 14

Slide 14 text

Ruby & Rails style guides

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Clojure Style Guide

Slide 19

Slide 19 text

Clojure: THE BAD PARTS by Bozhidar Batsov

Slide 20

Slide 20 text

Disclaimer

Slide 21

Slide 21 text

I love Clojure

Slide 22

Slide 22 text

I have a huge respect for Clojure’s team

Slide 23

Slide 23 text

Complaining is much easier than running a major project

Slide 24

Slide 24 text

Stewardship: The Sobering Parts https://www.youtube.com/watch?v=2y5Pv4yN0b0 must see

Slide 25

Slide 25 text

Clojure is still way better than JavaScript

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

Technical problems

Slide 29

Slide 29 text

((((((()))))))

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

Bloated clojure.core

Slide 32

Slide 32 text

char-escape-string

Slide 33

Slide 33 text

char-name-string

Slide 34

Slide 34 text

subs

Slide 35

Slide 35 text

clojure.string

Slide 36

Slide 36 text

fi le-seq & line-seq

Slide 37

Slide 37 text

clojure.java.io

Slide 38

Slide 38 text

xml-seq

Slide 39

Slide 39 text

char-array, double-array, etc

Slide 40

Slide 40 text

unchecked-*

Slide 41

Slide 41 text

bit-*

Slide 42

Slide 42 text

re-*

Slide 43

Slide 43 text

Odd API decisions

Slide 44

Slide 44 text

defn-

Slide 45

Slide 45 text

Where are def- and defmacro-?

Slide 46

Slide 46 text

(some even? '(1 2 3 4))

Slide 47

Slide 47 text

(some? even? '(1 2 3 4))

Slide 48

Slide 48 text

(not-any? even? '(1 2 3 4))

Slide 49

Slide 49 text

Tight coupling with Java

Slide 50

Slide 50 text

Most Clojure libs/tools depend directly on Java

Slide 51

Slide 51 text

clojure.java.io

Slide 52

Slide 52 text

clojure.io

Slide 53

Slide 53 text

ClojureCLR is basically abandonware

Slide 54

Slide 54 text

Hard to write portable code

Slide 55

Slide 55 text

#?(:clj (Clojure expression) :cljs (ClojureScript expression) :cljr (Clojure CLR expression) :default (fallthrough expression))

Slide 56

Slide 56 text

Clojure’s internals are outdated

Slide 57

Slide 57 text

Clojure’s older internals were never updated to use protocols

Slide 58

Slide 58 text

Obscure stacktraces

Slide 59

Slide 59 text

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: i-dont-exist in this context, compiling:(one_man_wiki/views.clj:18) at clojure.lang.Compiler.analyze(Compiler.java:6281) at clojure.lang.Compiler.analyze(Compiler.java:6223) at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618) at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5054) at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3674) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6453) at clojure.lang.Compiler.analyze(Compiler.java:6262) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443) at clojure.lang.Compiler.analyze(Compiler.java:6262) at clojure.lang.Compiler.access$100(Compiler.java:37) at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:518) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455) at clojure.lang.Compiler.analyze(Compiler.java:6262) at clojure.lang.Compiler.analyze(Compiler.java:6223) at clojure.lang.Compiler.eval(Compiler.java:6515) at clojure.lang.Compiler.load(Compiler.java:6952) at clojure.lang.RT.loadResourceScript(RT.java:359) at clojure.lang.RT.loadResourceScript(RT.java:350) at clojure.lang.RT.load(RT.java:429) at clojure.lang.RT.load(RT.java:400) at clojure.core$load$fn__4890.invoke(core.clj:5415) at clojure.core$load.doInvoke(core.clj:5414) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.core$load_one.invoke(core.clj:5227) at clojure.core$load_lib.doInvoke(core.clj:5264) at clojure.lang.RestFn.applyTo(RestFn.java:142) at clojure.core$apply.invoke(core.clj:603) at clojure.core$load_libs.doInvoke(core.clj:5298) at clojure.lang.RestFn.applyTo(RestFn.java:137) at clojure.core$apply.invoke(core.clj:603) at clojure.core$require.doInvoke(core.clj:5381) at clojure.lang.RestFn.invoke(RestFn.java:457) at one_man_wiki.handler$eval1564$loading__4784__auto____1565.invoke(handler.clj:1) at one_man_wiki.handler$eval1564.invoke(handler.clj:1) at clojure.lang.Compiler.eval(Compiler.java:6511) at clojure.lang.Compiler.eval(Compiler.java:6501) at clojure.lang.Compiler.load(Compiler.java:6952) at clojure.lang.RT.loadResourceScript(RT.java:359) at clojure.lang.RT.loadResourceScript(RT.java:350) at clojure.lang.RT.load(RT.java:429) at clojure.lang.RT.load(RT.java:400) at clojure.core$load$fn__4890.invoke(core.clj:5415) at clojure.core$load.doInvoke(core.clj:5414) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.core$load_one.invoke(core.clj:5227) at clojure.core$load_lib.doInvoke(core.clj:5264) at clojure.lang.RestFn.applyTo(RestFn.java:142) at clojure.core$apply.invoke(core.clj:603) at clojure.core$load_libs.doInvoke(core.clj:5298) at clojure.lang.RestFn.applyTo(RestFn.java:137) at clojure.core$apply.invoke(core.clj:603) at clojure.core$require.doInvoke(core.clj:5381) at clojure.lang.RestFn.invoke(RestFn.java:421) at user$eval1.invoke(NO_SOURCE_FILE:1) at clojure.lang.Compiler.eval(Compiler.java:6511) at clojure.lang.Compiler.eval(Compiler.java:6500) at clojure.lang.Compiler.eval(Compiler.java:6477) at clojure.core$eval.invoke(core.clj:2797) at clojure.main$eval_opt.invoke(main.clj:297) at clojure.main$initialize.invoke(main.clj:316) at clojure.main$null_opt.invoke(main.clj:349) at clojure.main$main.doInvoke(main.clj:427) at clojure.lang.RestFn.invoke(RestFn.java:421) at clojure.lang.Var.invoke(Var.java:419) at clojure.lang.AFn.applyToHelper(AFn.java:163) at clojure.lang.Var.applyTo(Var.java:532) at clojure.main.main(main.java:37) Caused by: java.lang.RuntimeException: Unable to resolve symbol: i-dont-exist in this context at clojure.lang.Util.runtimeException(Util.java:170) at clojure.lang.Compiler.resolveIn(Compiler.java:6766) at clojure.lang.Compiler.resolve(Compiler.java:6710) at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6671) at clojure.lang.Compiler.analyze(Compiler.java:6244) ... 66 more

Slide 60

Slide 60 text

(defn no-such-variable [] i-dont-exist)

Slide 61

Slide 61 text

API docs

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

No.

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

The docstring format is bad

Slide 66

Slide 66 text

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

Slide 67

Slide 67 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 68

Slide 68 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 69

Slide 69 text

Comparison with Emacs Lisp

Slide 70

Slide 70 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 71

Slide 71 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 72

Slide 72 text

Namespace documentation

Slide 73

Slide 73 text

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

Slide 74

Slide 74 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 75

Slide 75 text

Some def macros don’t support docstrings directly

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

This shouldn’t be hard to fi x, right?

Slide 78

Slide 78 text

No uni fi ed convention about documentation-related metadata usage

Slide 79

Slide 79 text

How do you handle deprecations?

Slide 80

Slide 80 text

How do you indicate when something was introduced/ changed?

Slide 81

Slide 81 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 82

Slide 82 text

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

Slide 83

Slide 83 text

What’s the metadata every function should have?

Slide 84

Slide 84 text

And what about each namespace?

Slide 85

Slide 85 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 speci fi cation

Slide 86

Slide 86 text

Specifying indentation

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

(do (something) (quick))

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

Stewardship

Slide 94

Slide 94 text

Contributing is hard

Slide 95

Slide 95 text

A lot of decisions are made behind closed doors

Slide 96

Slide 96 text

Unpredictable release schedule

Slide 97

Slide 97 text

(Almost) No bug fi x releases

Slide 98

Slide 98 text

clojure-contrib

Slide 99

Slide 99 text

Projects in permanent alpha state

Slide 100

Slide 100 text

No projects ever get promoted to Clojure itself

Slide 101

Slide 101 text

Most contrib projects have between 0 and 1 maintainers

Slide 102

Slide 102 text

No content

Slide 103

Slide 103 text

Contrib is a failed experiment that has to be shutdown

Slide 104

Slide 104 text

Documentation???

Slide 105

Slide 105 text

clojure.org

Slide 106

Slide 106 text

No content

Slide 107

Slide 107 text

No content

Slide 108

Slide 108 text

No content

Slide 109

Slide 109 text

clojuredocs.org

Slide 110

Slide 110 text

clojure-doc.org

Slide 111

Slide 111 text

conj.io

Slide 112

Slide 112 text

No content

Slide 113

Slide 113 text

Something’s de fi nitely wrong with Clojure’s documentation…

Slide 114

Slide 114 text

The Problems

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

It’s hard to contribute simple documentation fi xes

Slide 117

Slide 117 text

Relax the rules for documentation improvements

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

clojure.org already does this!!!

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

Clojure itself doesn’t

Slide 122

Slide 122 text

Epilogue

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

Dunaj (http://www.dunaj.org)

Slide 126

Slide 126 text

Jaunt (https://github.com/jaunt-lang/jaunt)

Slide 127

Slide 127 text

File tickets

Slide 128

Slide 128 text

Send patches

Slide 129

Slide 129 text

Blog about the issues

Slide 130

Slide 130 text

Speak about the issues

Slide 131

Slide 131 text

Let’s make Clojure awesome together!

Slide 132

Slide 132 text

Let’s make Clojure better together!

Slide 133

Slide 133 text

Let’s make Clojure more awesome together!

Slide 134

Slide 134 text

Clojure Clojure

Slide 135

Slide 135 text

Felina

Slide 136

Slide 136 text

Epilogue twitter: @bbatsov github: @bbatsov http//batsov.com http://emacsredux.com Partical Conf So fi a, Bulgaria 22.09.2017