├── README.md ├── README.zh.md └── img ├── calls.png ├── cljs.png └── sublime-cljs-syntax.png /README.md: -------------------------------------------------------------------------------- 1 | ![cljs](img/cljs.png) 2 | 3 | > Translations: [中文](README.zh.md). 4 | 5 | > __If you just wanna try something__, check out the [interactive tutorial](http://chimeces.com/cljs-browser-repl/). 6 | 7 | Hello, this is my attempt at a very concise guide to ClojureScript's syntax! 8 | ClojureScript is a Lisp dialect for front-end web development. It compiles to 9 | JavaScript for use in the browser. 10 | 11 | ClojureScript is fundamentally different from JavaScript and other 12 | compile-to-JS languages like Dart, CoffeeScript, and TypeScript. It uses a 13 | more powerful yet simpler syntax. There are other differences not related 14 | to syntax, such as default immutability to combat the "new spaghetti code" 15 | that is mutatable stateful objects, and sane state management allowing language-level data-binding. 16 | 17 | I believe that ClojureScript's largest barrier to entry for beginners is 18 | probably the foreign nature of its syntax. I hope to explain it as plainly and 19 | succinctly as possible with this guide. 20 | 21 | > __Also__, check out [Parinfer] if you want a simpler way to manage parentheses in ClojureScript. 22 | 23 | [Parinfer]:http://shaunlebron.github.io/parinfer/ 24 | 25 | ## Syntax 26 | 27 | There is __literal data__: 28 | 29 | ```clj 30 | ; number 31 | 1.23 32 | 33 | ; string 34 | "foo" 35 | 36 | ; keyword (like strings, but used as map keys) 37 | :foo 38 | 39 | ; vector (array) 40 | [:bar 3.14 "hello"] 41 | 42 | ; map (associative array) 43 | {:msg "hello" :pi 3.14 :primes [2 3 5 7 11 13]} 44 | 45 | ; set (distinct elements) 46 | #{:bar 3.14 "hello"} 47 | ``` 48 | 49 | And there is __symbolic data__: 50 | 51 | ```clj 52 | ; symbol (represents a named value) 53 | foo 54 | 55 | ; list (represents a "call") 56 | (foo :bar 3.14) 57 | ``` 58 | 59 | #### Evaluation 60 | 61 | ClojureScript can evaluate data to create a new "value" from it. 62 | 63 | 1. Literal data evaluates to itself, of course: 64 | 65 | ```clj 66 | 1.23 ; => 1.23 67 | "foo" ; => "foo" 68 | [:bar 3.14 "hello"] ; => [:bar 3.14 "hello"] 69 | ``` 70 | 71 | 1. A __symbol__ evaluates to the value bound to it: 72 | 73 | ```clj 74 | foo ; => 3 75 | ``` 76 | 77 | 1. A __list__ evaluates to the return value of a "call". 78 | 79 | ```clj 80 | (+ 1 2 3) ; => 6 81 | (= 1 2) ; => false 82 | (if true "y" "n") ; => "y" 83 | ``` 84 | 85 | #### Calls 86 | 87 | If the first element of a list is a __function__, then the rest of the elements 88 | are evaluated and passed to it ([prefix notation](http://en.wikipedia.org/wiki/Polish_notation)). 89 | 90 | ```clj 91 | ; String concatenate function 92 | (str "Hello " "World") ; => "Hello World" 93 | 94 | ; Arithmetic functions 95 | (= a b) ; equality (true or false) 96 | (+ a b) ; sum 97 | (- a b) ; difference 98 | (* a b c) ; product 99 | (< a b c) ; true if a < b < c 100 | 101 | ; Evaluation Steps 102 | (+ k (* 2 4)) ; assume k evalutes to 3 103 | (+ 3 (* 2 4)) ; (* 2 4) evaluates to 8 104 | (+ 3 8) ; (+ 3 8) evalutes to 11 105 | 11 106 | ``` 107 | 108 | If the first element of a list is one of the language's few __special forms__, 109 | then the rest of the elements are passed to it unevaluated. (There are only [22 110 | special forms](https://clojure.org/reference/special_forms).) 111 | 112 | ```clj 113 | (if (= a b c) ; <-- determines if a=b=c 114 | (foo 1) ; <-- only evaluated if true 115 | (bar 2) ; <-- only evaluated if false 116 | ) 117 | 118 | ; define k as 3 119 | (def k 3) ; <-- notice that k is not evaluated here 120 | ; (def needs the symbol k, not its value) 121 | 122 | ; make a greeting function 123 | (fn [username] ; <-- expected parameters vector 124 | (str "Hello " username)) 125 | 126 | ; oops, give the function a name 127 | (def greet (fn [username] 128 | (str "Hello " username))) 129 | 130 | (greet "Bob") ; => "Hello Bob" 131 | ``` 132 | 133 | If the first element of a list is a __macro__, then the rest of the elements 134 | are passed to it unevaluated, but the resulting value of the call is evaluated. 135 | Let's illustrate that difference with the following diagram: 136 | 137 | ![calls](img/calls.png) 138 | 139 | This difference in evaluation allows macros to act as code-generating 140 | functions. For example, the `defn` macro expands to `def` and `fn`, as we used 141 | separately in a previous example: 142 | 143 | ```clj 144 | ; create a named function using the defn macro 145 | (defn greet [username] 146 | (str "Hello " username)) 147 | 148 | ; the definition for the defn macro (over-simplified) 149 | (defmacro defn [name args body] 150 | `(def ~name (fn ~args ~body))) 151 | ``` 152 | 153 | __App developers rarely need to create their own macros__, but it is an 154 | indispensible tool for the library developer to give app developers the full 155 | flexibility of the language. 156 | 157 | #### Simple substitutions 158 | 159 | There are a few [macro characters](http://clojure.org/reader#The%20Reader--Macro%20characters) that help make the language succinct 160 | by performing simple substitutions (not full macros): 161 | 162 | ```clj 163 | ; short-hand for creating a simple function: 164 | ; #(...) => (fn [args] (...)) 165 | 166 | #(* 3 %) ; => (fn [x] (* 3 x)) 167 | 168 | #(* 3 (+ %1 %2)) ; => (fn [x y] (* 3 (+ x y))) 169 | ``` 170 | 171 | ## That's it for Syntax 172 | 173 | You need to know more than syntax to be proficient in a language, of course. 174 | But you should now know enough to be comfortable looking around at examples and 175 | reasoning about how data is being evaluated and passed around: 176 | 177 | ```clj 178 | ; printing to the javascript console 179 | (js/console.log "Hello World!") 180 | 181 | ; creating local bindings (constants) 182 | (let [a (+ 1 2) 183 | b (* 2 3)] 184 | (js/console.log "The value of a is" a) 185 | (js/console.log "The value of b is" b)) 186 | 187 | ; generate a sequence of numbers 188 | (range 4) ; => (0 1 2 3) 189 | 190 | ; generate first four multiples of 3 191 | (map #(* % 3) (range 4)) ;=> (0 3 6 9) 192 | 193 | ; count elements in a sequence 194 | (count "Bob") ; => 3 195 | (count [4 5 2 3]) ; => 4 196 | 197 | ; select three letter names from a list 198 | (def names ["Bob" "David" "Sue"]) 199 | (filter #(= (count %) 3) names) ; => ("Bob" "Sue") 200 | ``` 201 | 202 | (Don't worry about getting lost in parentheses. All modern text editors will 203 | highlight the corresponding ones and will indent your code automatically for 204 | readability, as is standard in every other language.) 205 | 206 | ## ClojureScript vs JSX in HTML templating 207 | 208 | See [Jumping from HTML to ClojureScript](https://github.com/shaunlebron/jumping-from-html-to-clojurescript) to see how ClojureScript's syntax solves the verbosity/flexibility problems faced in the JS community by [JSX]. 209 | 210 | [Jumping from HTML to ClojureScript]:https://github.com/shaunlebron/jumping-from-html-to-clojurescript 211 | [JSX]:https://facebook.github.io/react/docs/jsx-in-depth.html 212 | 213 | ## A complete reference 214 | 215 | The syntax section of the [ClojureScript API reference] is a comprehensive look at 216 | the possible syntax forms and even shows the source code for how each are read/parsed. 217 | 218 | [syntax section]:https://github.com/cljsinfo/cljs-api-docs/blob/catalog/INDEX.md#syntax 219 | [ClojureScript API reference]:http://cljs.github.io/api 220 | 221 | ## Useful Resources 222 | 223 | Here are the resources and steps that I took while learning ClojureScript. 224 | (Most resources covering Clojure also apply to ClojureScript, since they 225 | share a significant subset with each other.) 226 | 227 | 1. Reading through the [ClojureScript Wiki](https://github.com/clojure/clojurescript/wiki) 228 | 1. Reading the book [ClojureScript Up and Running](http://ns.synrc.com/publications/cat/Functional%20Languages/Clojure/Oreilly.ClojureScript.Up.and.Running.Oct.2012.pdf) 229 | 1. Reading the book [Clojure Programming](http://bit.ly/clojurebook) 230 | 1. Doing [ClojureScript Koans](http://clojurescriptkoans.com) 231 | 1. Reading [Clojure Style Guide](https://github.com/bbatsov/clojure-style-guide) 232 | 1. Reading [Clojure Programming By Example](http://en.wikibooks.org/wiki/Clojure_Programming/By_Example) 233 | 1. Reading [Clojure Functional Programming](http://clojure.org/functional_programming) 234 | 1. Thumbing through [Clojure Core API](http://clojure.github.io/clojure/clojure.core-api.html) 235 | 1. Reading [ClojureScript - Differences from Clojure - Host Interop](https://github.com/clojure/clojurescript/wiki/Differences-from-Clojure#wiki-host-interop) for accessing javascript properties like `(.-Infinity js/window)` and functions like `(.sqrt js/Math 25)`. 236 | 1. Reading [JavaScript to ClojureScript synonyms](http://kanaka.github.io/clojurescript/web/synonym.html) 237 | 1. Experimenting in `lein repl` for Clojure REPL. 238 | 1. Experimenting in for ClojureScript REPL with a browser context. 239 | 1. Reading docstrings of functions I encounter with `(doc )` in REPL. 240 | 1. [Miscellaneous ClojureScript things to know](https://github.com/shaunlebron/ClojureSheet#clojurescript-stuff) 241 | 242 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | ![cljs](img/cljs.png) 2 | 3 | > __如果你只是想试试__, 可以看看这个[交互式教程](http://chimeces.com/cljs-browser-repl/). 4 | 5 | Hello, 我在尝试给 ClojureScript 语法写一份简明的教程! 6 | ClojureScript 是一门适用于 Web 前端开发的 Lisp 方言, 它被编译到 JavaScript 在浏览器中使用. 7 | 8 | ClojureScript 在根本上就和 JavaScript 和其他诸如 Dart, CoffeeScript 和 TypeScript 之类的编译到 JavaScript 的语言不一样. 9 | 它使用了更强大而简单的语法. 也有一些其他一些语法之外的区别, 10 | 比如说默认采用不可变性, 用来解决状态可变的对象造成"新的意面式代码", 11 | 同时提供正常的状态管理功能用来实现语言级别的数据绑定. 12 | 13 | 我相信对于新手来说 ClojureScript 最大的障碍在于他令人感到陌生的语法. 14 | 在这份教程里, 我希望能尽可能清晰而简明地说明它. 15 | 16 | > __同时__, 如果你需要为 ClojureScript 找一个更简单的管理括号的方案, 请了解一下 [Parinfer]. 17 | 18 | [Parinfer]:http://shaunlebron.github.io/parinfer/ 19 | 20 | ## 语法 21 | 22 | 这是 __literal data(字面量数据)__: 23 | 24 | ```clj 25 | ; number(数字) 26 | 1.23 27 | 28 | ; string(字符串) 29 | "foo" 30 | 31 | ; keyword(关键字, 就像字符串, 但是用于 map 的键) 32 | :foo 33 | 34 | ; vector(向量, 或者说数组, array) 35 | [:bar 3.14 "hello"] 36 | 37 | ; map(关联数组, associative array) 38 | {:msg "hello" :pi 3.14 :primes [2 3 5 7 11 13]} 39 | 40 | ; set(distinct elements, 元素唯一) 41 | #{:bar 3.14 "hello"} 42 | ``` 43 | 44 | 还有 __symbolic data(符号化数据)__: 45 | 46 | ```clj 47 | ; symbol(符号, 表示一个有名字的值) 48 | foo 49 | 50 | ; list(链表, 表示一次"调用") 51 | (foo :bar 3.14) 52 | ``` 53 | 54 | #### 求值 55 | 56 | ClojureScript 可以对数据求值从而创建出新的"值". 57 | 58 | 1. 字面量数据求值之后得到自身, 显然地: 59 | 60 | ```clj 61 | 1.23 ; => 1.23 62 | "foo" ; => "foo" 63 | [:bar 3.14 "hello"] ; => [:bar 3.14 "hello"] 64 | ``` 65 | 66 | 1. __符号__ 求值之后得到绑定在上面的数值: 67 | 68 | ```clj 69 | foo ; => 3 70 | ``` 71 | 72 | 1. __list__ 求值之后得到"调用"的结果. 73 | 74 | ```clj 75 | (+ 1 2 3) ; => 6 76 | (= 1 2) ; => false 77 | (if true "y" "n") ; => "y" 78 | ``` 79 | 80 | #### 调用 81 | 82 | 如果列表的第一个元素是一个 __函数__, 那么其余元素会被求值并传给它 ([prefix notation](http://en.wikipedia.org/wiki/Polish_notation)). 83 | 84 | ```clj 85 | ; 字符串合并函数 86 | (str "Hello " "World") ; => "Hello World" 87 | 88 | ; 算术函数 89 | (= a b) ; 等式(true 或者 false) 90 | (+ a b) ; 求和 91 | (- a b) ; 求差 92 | (* a b c) ; 求积 93 | (< a b c) ; true if a < b < c 94 | 95 | ; 求值步骤 96 | (+ k (* 2 4)) ; 这里假设 k 求值得到 3 97 | (+ 3 (* 2 4)) ; (* 2 4) 求值得到 8 98 | (+ 3 8) ; (+ 3 8) 求值得到 11 99 | 11 100 | ``` 101 | 102 | 如果列表的第一个元素是一个 __特殊形式(Special Form)__, 那么其余元素传递给它时不做求值. 103 | (总共有 [22 的特殊形式](https://clojure.org/reference/special_forms).) 104 | 105 | ```clj 106 | (if (= a b c) ; <-- 判断是否 a=b=c 107 | (foo 1) ; <-- 只在 true 的时候求值 108 | (bar 2) ; <-- 只在 false 的时候求值 109 | ) 110 | 111 | ; 定义(define) k 为 3 112 | (def k 3) ; <-- 注意这个 k 不会被求值 113 | ; (def 需要 k 这个符号, 而不是它的值) 114 | 115 | ; 创建一个打招呼的函数 116 | (fn [username] ; <-- 所期望的参数向量 117 | (str "Hello " username)) 118 | 119 | ; 哦对了,给这个函数取个名字 120 | (def greet (fn [username] 121 | (str "Hello " username))) 122 | 123 | (greet "Bob") ; => "Hello Bob" 124 | ``` 125 | 126 | 如果列表的第一个元素是个 __Macro__, 那么其余元素传给它时不做求值, 127 | 但是调用之后的结果是经过求值的. 用下面这个示意图来说明它们的区别: 128 | 129 | ![calls](img/calls.png) 130 | 131 | 求值过程上的区别使得 Macro 可以作为生成代码的函数使用. 132 | 比如 `defn` 这个 Macro 展开成了 `def` 和 `fn`,前面例子中我们是分开使用的: 133 | 134 | ```clj 135 | ; 用 defn 这个 Macro 创建一个具名函数 136 | (defn greet [username] 137 | (str "Hello " username)) 138 | 139 | ; defn 这个 Macro 的定义(极度简化版本) 140 | (defmacro defn [name args body] 141 | `(def ~name (fn ~args ~body))) 142 | ``` 143 | 144 | __应用开发者极少需要为他们自己创建 Macros__, 但这项功能对于类库开发者来说是不可或缺的, 145 | 通过 Macro 他们可以为应用开发者提供语言本身最大的灵活性. 146 | 147 | #### 简单的替换 148 | 149 | 有几个 [Macro 字符](http://clojure.org/reader#The%20Reader--Macro%20characters)被用来进行简单的替换, 这使语言变得更简洁(不全是 Macro). 150 | 151 | ```clj 152 | ; 简单函数的一种简写 153 | ; #(...) => (fn [args] (...)) 154 | 155 | #(* 3 %) ; => (fn [x] (* 3 x)) 156 | 157 | #(* 3 (+ %1 %2)) ; => (fn [x y] (* 3 (+ x y))) 158 | ``` 159 | 160 | ## 就这些语法了 161 | 162 | 要熟练使用 ClojureScript 仅仅了解语法是不够的. 163 | 不过了解了上面这些语法, 你应该可以比较轻松地查看各种例子了, 164 | 也可以推理数据是怎么被求值的, 怎么被到处传递的. 165 | 166 | ```clj 167 | ; 在 JavaScript Console 中打印 168 | (js/console.log "Hello World!") 169 | 170 | ; 创建局部的绑定(常量) 171 | (let [a (+ 1 2) 172 | b (* 2 3)] 173 | (js/console.log "The value of a is" a) 174 | (js/console.log "The value of b is" b)) 175 | 176 | ; 创建数字的序列 177 | (range 4) ; => (0 1 2 3) 178 | 179 | ; 生成前 4 个自然数乘以 3 的结果 180 | (map #(* % 3) (range 4)) ;=> (0 3 6 9) 181 | 182 | ; 序列中的元素个数 183 | (count "Bob") ; => 3 184 | (count [4 5 2 3]) ; => 4 185 | 186 | ; 从列表中筛选出三个字母组成的名字 187 | (def names ["Bob" "David" "Sue"]) 188 | (filter #(= (count %) 3) names) ; => ("Bob" "Sue") 189 | ``` 190 | 191 | (不需要担心圆括号太多而迷失. 所有的现代文本编辑器都可以做到配对的括号的高亮, 甚至自动缩进代码来保证可读性, 就跟其他语言标准的做法一样.) 192 | 193 | ## HTML 模板语法中的 ClojureScript 对比 JSX 194 | 195 | 阅读 [Jumping from HTML to ClojureScript](https://github.com/shaunlebron/jumping-from-html-to-clojurescript) 来了解 ClojureScript 语法怎样解决 JavaScript 社区中 JSX 解决的冗长性(verbosity?)和灵活性的问题. 196 | 197 | [Jumping from HTML to ClojureScript]:https://github.com/shaunlebron/jumping-from-html-to-clojurescript 198 | [JSX]:https://facebook.github.io/react/docs/jsx-in-depth.html 199 | 200 | ## 完整的手册 201 | 202 | [ClojureScript API 手册]的语法章节可以找到所有可能的语法形式, 甚至还显示了源码怎样进行读取和解析. 203 | 204 | [syntax section]:https://github.com/cljsinfo/cljs-api-docs/blob/catalog/INDEX.md#syntax 205 | [ClojureScript API 手册]:http://cljs.github.io/api 206 | 207 | ## 实用资源 208 | 209 | 下面是我在学习 ClojureScript 时候的一些资源和步骤. (大部分介绍 Clojure 的资源对于 ClojureScript 也适用, 因为两者共享了很大的交集.) 210 | 211 | 1. Reading through the [ClojureScript Wiki](https://github.com/clojure/clojurescript/wiki) 212 | 1. Reading the book [ClojureScript Up and Running](http://synrc.com/publications/cat/Functional%20Languages/Clojure/Oreilly.ClojureScript.Up.and.Running.Oct.2012.pdf) 213 | 1. Reading the book [Clojure Programming](http://bit.ly/clojurebook) 214 | 1. Doing [ClojureScript Koans](http://clojurescriptkoans.com) 215 | 1. Reading [Clojure Style Guide](https://github.com/bbatsov/clojure-style-guide) 216 | 1. Reading [Clojure Programming By Example](http://en.wikibooks.org/wiki/Clojure_Programming/By_Example) 217 | 1. Reading [Clojure Functional Programming](http://clojure.org/functional_programming) 218 | 1. Thumbing through [Clojure Core API](http://clojure.github.io/clojure/clojure.core-api.html) 219 | 1. Reading [ClojureScript - Differences from Clojure - Host Interop](https://github.com/clojure/clojurescript/wiki/Differences-from-Clojure#wiki-host-interop) for accessing javascript properties like `(.-Infinity js/window)` and functions like `(.sqrt js/Math 25)`. 220 | 1. Reading [JavaScript to ClojureScript synonyms](http://kanaka.github.io/clojurescript/web/synonym.html) 221 | 1. Experimenting in `lein repl` for Clojure REPL. 222 | 1. Experimenting in for ClojureScript REPL with a browser context. 223 | 1. Reading docstrings of functions I encounter with `(doc )` in REPL. 224 | 1. [Miscellaneous ClojureScript things to know](https://github.com/shaunlebron/ClojureSheet#clojurescript-stuff) 225 | -------------------------------------------------------------------------------- /img/calls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaunlebron/ClojureScript-Syntax-in-15-minutes/7dec81134163c5c6a42a8f8b30762afa3c1347fe/img/calls.png -------------------------------------------------------------------------------- /img/cljs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaunlebron/ClojureScript-Syntax-in-15-minutes/7dec81134163c5c6a42a8f8b30762afa3c1347fe/img/cljs.png -------------------------------------------------------------------------------- /img/sublime-cljs-syntax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaunlebron/ClojureScript-Syntax-in-15-minutes/7dec81134163c5c6a42a8f8b30762afa3c1347fe/img/sublime-cljs-syntax.png --------------------------------------------------------------------------------