|
| 1 | += The Weird and Wonderful Characters of Clojure |
| 2 | +James Hughes |
| 3 | +2017-05-27 |
| 4 | +:type: guides |
| 5 | +:toc: macro |
| 6 | + |
| 7 | +ifdef::env-github,env-browser[:outfilesuffix: .adoc] |
| 8 | + |
| 9 | +[] |
| 10 | +==== |
| 11 | +This is a reference collection of characters used in Clojure that are difficult to "google". |
| 12 | +Descriptions are sourced from various blogs, https://stackoverflow.com[StackOverflow], |
| 13 | +http://en.wikibooks.org/wiki/Learning_Clojure[Learning Clojure], and the |
| 14 | +http://clojure.org/documentation[official Clojure docs] -- sources attributed |
| 15 | +where necessary. Sections are not in any particular order, but related items |
| 16 | +are grouped for ease. This guide is a copy of http://twitter.com/kouphax[James Hughes] |
| 17 | +brilliant https://yobriefca.se/blog/2014/05/19/the-weird-and-wonderful-characters-of-clojure/[blog post] of the same name. |
| 18 | +==== |
| 19 | + |
| 20 | +== # - Dispatch macro |
| 21 | + |
| 22 | +You'll see this macro character beside another e.g. `\#(` or `#"`. |
| 23 | +// " Comment needed for emacs to behave. |
| 24 | +This topic will act as a bit of a preamble before looking at your specific case. |
| 25 | + |
| 26 | +`#` is the dispatch macro, a reader macro that tells the Clojure |
| 27 | +reader (the thing that takes a file of Clojure code and parses it for |
| 28 | +consumption by the compiler) to go and look at another *read table* |
| 29 | +for the definition of the next character - in essence this allows |
| 30 | +extending default reader behaviour. |
| 31 | + |
| 32 | +Clojure doesn't provide support for creating reader macros, but it is possible |
| 33 | +through http://briancarper.net/blog/449/[a bit of hackery]. |
| 34 | + |
| 35 | +If you se `#` *at the end* of a symbol, then it is used to automatically |
| 36 | +generate a new symbol. This is useful inside macros to keep macro specifics |
| 37 | +from leaking into the userspace. A regulare `let` will fail in a macro definition |
| 38 | + |
| 39 | +[source,clojure] |
| 40 | +---- |
| 41 | +user=> (defmacro m [] `(let [x 1] x)) |
| 42 | +#'user/m |
| 43 | +user=> (m) |
| 44 | +CompilerException java.lang.RuntimeException: Can't let qualified name: user/x, compiling:(NO_SOURCE_PATH:1) |
| 45 | +---- |
| 46 | + |
| 47 | +Instead you need to append `#` to the end of the variable name and let Clojure |
| 48 | +generate a unique symbol for it: |
| 49 | + |
| 50 | +[source, clojure] |
| 51 | +---- |
| 52 | +user=> (defmacro m [] `(let [x# 1] x#)) |
| 53 | +#'user/m |
| 54 | +user=> (m) |
| 55 | +1 |
| 56 | +user=> |
| 57 | +---- |
| 58 | + |
| 59 | +If we expand this macro, we can see the `gensym` 'd name: |
| 60 | + |
| 61 | +[source, clojure] |
| 62 | +---- |
| 63 | +user=> (macroexpand '(m)) |
| 64 | +(let* [x__681__auto__ 1] x__681__auto__) |
| 65 | +---- |
| 66 | + |
| 67 | +Another place you'll see the `#` is in |
| 68 | +http://clojure.org/reader#The%20Reader--Tagged%20Literals[tagged literals]. |
| 69 | +Most commonly you'll see this use in https://github.com/edn-format/edn[EDN] |
| 70 | +(extensible data notation - a rich data fromat that can be used in Clojure) |
| 71 | +and in ClojureScript (`#js`). Search for `#inst`, `#uuid`, or `#js` for some |
| 72 | +more info. |
| 73 | + |
| 74 | +* http://clojure.org/reader[Clojure Documentation Reader] |
| 75 | +* http://briancarper.net/blog/449/[Clojure Reader Macros] |
| 76 | +* http://clojuredocs.org/clojure_core/clojure.core/gensym[ClojureDocs - gensyms] |
| 77 | + |
| 78 | +== #{ - Set macro |
| 79 | + |
| 80 | +See the dispatch (`\#`) macro for additional details. |
| 81 | + |
| 82 | +`#{` defines a set (a collection of unique values) specifically a `hash-set`. The |
| 83 | +following are equivalent: |
| 84 | + |
| 85 | +[source, clojure] |
| 86 | +---- |
| 87 | +user=> #{1 2 3 4} |
| 88 | +#{1 2 3 4} |
| 89 | +user=> (hash-set 1 2 3 4) |
| 90 | +#{1 2 3 4} |
| 91 | +---- |
| 92 | + |
| 93 | +Attempting to create a `set` using this literal form will throw if there |
| 94 | +are duplicates. Instead the `hash-set` function should be used on a vector. |
| 95 | + |
| 96 | +[source, clojure] |
| 97 | +---- |
| 98 | +user=> #{1 2 3 4 1} |
| 99 | +
|
| 100 | +IllegalArgumentException Duplicate key: 1 clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:68) |
| 101 | +user=> (set [1 2 3 4 1]) ; convert vector to set, removing duplicates |
| 102 | +#{1 2 3 4} |
| 103 | +---- |
| 104 | + |
| 105 | +* http://clojure.org/data_structures#Data%20Structures-Sets[Clojure Documentation: Sets] |
| 106 | + |
| 107 | +== #_ - Discard macro |
| 108 | + |
| 109 | +See the dispatch (`#`) macro for additional details. |
| 110 | + |
| 111 | +`#_` tells the reader to ignore the next form completely. |
| 112 | + |
| 113 | +[source,clojure] |
| 114 | +---- |
| 115 | +user=> [1 2 3 #_ 4 5] |
| 116 | +[1 2 3 5] |
| 117 | +---- |
| 118 | +The docs suggest that "The form following `#_` is completely skipped by the reader, |
| 119 | +(This is a more complete removal than the `comments` macro which yields `nil`).". |
| 120 | +This can prove useful for debugging situations or for multiline comments. |
| 121 | + |
| 122 | +* http://clojure.org/reader[Clojure Documentation - Reader |
| 123 | + |
| 124 | +== #" - Regular Expression macro |
| 125 | +// " for the pleasure of emacs. |
| 126 | + |
| 127 | +See the dispatch (`#`) macro for additional details. |
| 128 | + |
| 129 | +`#"` indicates the start of a regular expression |
| 130 | +// " |
| 131 | +[source,clojure] |
| 132 | +---- |
| 133 | +user=> (re-matches #"^test$" "test") |
| 134 | +"test" |
| 135 | +---- |
| 136 | + |
| 137 | +This form is compiled at *read time* into a host-specific regex machinery. |
| 138 | + |
| 139 | +* http://clojure.org/other_functions#Other%20Useful%20Functions%20and%20Macros-Regex%20Support[Clojure Documentation: Regex Support] |
| 140 | + |
| 141 | +== #( - Function macro |
| 142 | + |
| 143 | +See the dispatch (`#`) macro for additional details. |
| 144 | + |
| 145 | +`#(` begins the short hand syntax for an inline function definition. The |
| 146 | +following two snippets of code are similar: |
| 147 | + |
| 148 | +[source,clojure] |
| 149 | +---- |
| 150 | +; anonymous function takin a single argument and printing it |
| 151 | +(fn [line] (println line)) ; |
| 152 | +
|
| 153 | +; anonymous function takin a single argument and printing it - shorthand |
| 154 | +#(println %) |
| 155 | +---- |
| 156 | + |
| 157 | +The macro expands the shorthand syntax into a function definition whose |
| 158 | +arity (the number of arguments it takes) is defined by how the `%` placeholders |
| 159 | +are declared. See the `%` character for discussion around arity. |
| 160 | + |
| 161 | +[source,clojure] |
| 162 | +---- |
| 163 | +user=> (macroexpand `#(println %)) |
| 164 | +(fn* [arg] (clojure.core/println arg)) ; argument names shortened for clarity |
| 165 | +---- |
| 166 | + |
| 167 | +== #' - Var macro |
| 168 | + |
| 169 | +`#'` is the var quote. It is the same as the `var` function: |
| 170 | + |
| 171 | +[source,clojure] |
| 172 | +---- |
| 173 | +user=> (def nine 9) |
| 174 | +#'user/nine |
| 175 | +user=> nine |
| 176 | +9 |
| 177 | +user=> (var nine) |
| 178 | +#'user/nine |
| 179 | +user=> #'nine |
| 180 | +#'user/nine |
| 181 | +---- |
| 182 | +When used it will attempt to return the referenced var. This is useful when |
| 183 | +you want to talk ab out the reference/declaration instead of teh value it represents. |
| 184 | +See the use of `meta` int the metadata (`^`) discussion. |
| 185 | + |
| 186 | +* http://clojure.org/special_forms#var[Clojure Official Documentation: Special Forms] |
| 187 | + |
| 188 | +== #inst, #uuid, and #js etc. - tagged literals |
| 189 | + |
| 190 | +Commonly found in EDN and ClojureScript this use of `#` is called the _tagged literal_. |
| 191 | +Look at this example: |
| 192 | +[source,clojure] |
| 193 | +---- |
| 194 | +user=> (java.util.Date.) |
| 195 | +#inst "2014-05-19T19:12:37.925-00:00" |
| 196 | +---- |
| 197 | + |
| 198 | +When we create a new date it is represented as a tagged literal, or in this case, |
| 199 | +a tagged string. We can use Clojures `read-string` to read this back (or use it directly): |
| 200 | +[source,clojure] |
| 201 | +---- |
| 202 | +user=> (type #inst "2014-05-19T19:12:37.925-00:00") |
| 203 | +java.util.Date |
| 204 | +(read-string "#inst \"2014-05-19T19:12:37.925-00:00\"") |
| 205 | +#inst "2014-05-19T19:12:37.925-00:00" |
| 206 | +user=> (type (read-string "#inst \"2014-05-19T19:12:37.925-00:00\"")) |
| 207 | +java.util.Date |
| 208 | +---- |
| 209 | + |
| 210 | +A tagged literal tells the reader how to parse the literal value. Other common |
| 211 | +uses include `#uuid` for generating UUIDs and in the ClojureScript world an |
| 212 | +extremely common use of tagged literals is `#js` which can be used to convert |
| 213 | +ClojureScript data structures into JavaScript structures directly. Note that |
| 214 | +`#js` doesn't convert recursivly, so if you have a nested data-structure, use |
| 215 | +`cjs->js`. |
| 216 | + |
| 217 | +* https://github.com/edn-format/edn#tagged-elements[EDN Tagged Elements] |
| 218 | + |
| 219 | +== % - Argument placeholder |
| 220 | + |
| 221 | +`%` is not a macro, but a placeholder for use in the `#(` macro. It represents |
| 222 | +an argument that will be passed into the function when it is expanded. |
| 223 | +[source,clojure] |
| 224 | +---- |
| 225 | +user=> (macroexpand `#(println %)) |
| 226 | +(fn* [arg] (clojure.core/println arg)) ; takes a single arg, uses it once |
| 227 | +
|
| 228 | +user=> (macroexpand `#(println % %)) |
| 229 | +(fn* [arg] (clojure.core/println arg arg)) ; takes a single arg, uses it twice |
| 230 | +---- |
| 231 | +Numbers can be placed directly after the `%` to indicate the arguments position. |
| 232 | +Numbers are also used by the `#(` macro to determine the number of arguments |
| 233 | +to pass in. |
| 234 | +[source,clojure] |
| 235 | +---- |
| 236 | +user=> (macroexpand `#(println %1 %2)) |
| 237 | +(fn* [arg1 arg2] (clojure.core/println arg1 arg2)) ; takes 2 args |
| 238 | +
|
| 239 | +user=> (macroexpand `#(println %4)) |
| 240 | +(fn* [arg1 arg2 arg3 arg4] (clojure.core/println arg4)) ; takes 4 args doesn't use 3 |
| 241 | +---- |
| 242 | + |
| 243 | +You don't have to use the arguments, but you do need to declare them in the order |
| 244 | +you'd expect an external caller to pass them in. |
| 245 | + |
| 246 | +`%` and `%1` can be used interchangably: |
| 247 | +[source,clojure] |
| 248 | +---- |
| 249 | +user=> (macroexpand `#(println % %1)) ; use both % and %1 |
| 250 | +(fn* [arg1] (clojure.core/println arg1 arg1)) ; still only takes 1 argument |
| 251 | +---- |
| 252 | + |
| 253 | +== @ - Deref macro |
| 254 | + |
| 255 | +`@` is the deref macro, it is the shorthand equivalent of the `deref` function so |
| 256 | +these two forms are the same: |
| 257 | +[source,clojure] |
| 258 | +---- |
| 259 | +user=> (def x (atom 1)) |
| 260 | +#'user/x |
| 261 | +user=> @x |
| 262 | +1 |
| 263 | +user=> (deref x) |
| 264 | +1 |
| 265 | +user=> |
| 266 | +---- |
| 267 | +`@` is used to get the current value of a reference. The above example uses |
| 268 | +`@` to get the current value of an http://clojure.org/atoms[atom], but `@` can |
| 269 | +be applied to other things such as `future` s, `delay` s, `promises` s etc. to |
| 270 | +force computation and potentially block. |
| 271 | + |
| 272 | +== ^ - Metadata |
| 273 | + |
| 274 | +`^` is the metadata marker. Metadata is a map of values (with shorthand option) |
| 275 | +that can be attached to various forms in Clojure. This provides extra information |
| 276 | +for these forms and can b e used for documentation, compilation warnings, |
| 277 | +typehints, and other features. |
| 278 | +[source,clojure] |
| 279 | +---- |
| 280 | +user=> (def ^{ :debug true } five 5) ; meta map with single boolean value |
| 281 | +#'user/five |
| 282 | +---- |
| 283 | + |
| 284 | +We can access the metadata by the `meta` function which should be executed |
| 285 | +against the declaration itself (rather than the returned value): |
| 286 | +[source,clojure] |
| 287 | +---- |
| 288 | +user=> (def ^{ :debug true } five 5) |
| 289 | +#'user/five |
| 290 | +user=> (meta #'five) |
| 291 | +{:ns #<Namespace user>, :name five, :column 1, :debug true, :line 1, :file "NO_SOURCE_PATH"} |
| 292 | +---- |
| 293 | +As we have a single value here, we can use a shorthand notation for declaring |
| 294 | +the metadata `^:name` which is useful for flags, as the value will be set to true. |
| 295 | +[source,clojure] |
| 296 | +---- |
| 297 | +user=> (def ^:debug five 5) |
| 298 | +#'user/five |
| 299 | +user=> (meta #'five) |
| 300 | +{:ns #<Namespace user>, :name five, :column 1, :debug true, :line 1, :file "NO_SOURCE_PATH"} |
| 301 | +---- |
| 302 | +Another use of `^` is for type hints. These are used to tell the compiler what |
| 303 | +type the value will be and allow it to perform type specific optimiztions |
| 304 | +thus potentially making resultant code faster: |
| 305 | +[source,clojure] |
| 306 | +---- |
| 307 | +user=> (def ^Integer five 5) |
| 308 | +#'user/five |
| 309 | +user=> (meta #'five) |
| 310 | +{:ns #<Namespace user>, :name five, :column 1, :line 1, :file "NO_SOURCE_PATH", :tag java.lang.Integer} |
| 311 | +---- |
| 312 | +We can see in that example the `:tag` property is set. |
| 313 | + |
| 314 | +You can also stak the shorthand notations: |
| 315 | +[source,clojure] |
| 316 | +---- |
| 317 | +user=> (def ^Integer ^:debug ^:private five 5) |
| 318 | +#'user/five |
| 319 | +user=> (meta #'five) |
| 320 | +{:ns #<Namespace user>, :name five, :column 1, :private true, :debug true, :line 1, :file "NO_SOURCE_PATH", :tag java.lang.Integer} |
| 321 | +---- |
| 322 | + |
| 323 | +* http://clojure.org/metadata[Clojure Official Documentation: Metadata] |
| 324 | +* http://en.wikibooks.org/wiki/Learning_Clojure/Meta_Data[Learning Clojure: Meta Data] |
| 325 | + |
| 326 | +== ' - Quote macro |
| 327 | + |
| 328 | +Can be used against symbols as part of a dispatch macro (see `#'`). Also |
| 329 | +used to quote forms and prevent their evalutation as with the quote function. |
| 330 | +[source,clojure] |
| 331 | +---- |
| 332 | +user=> (1 3 4) ; fails as it tries to evaluate 1 as a function |
| 333 | +
|
| 334 | +ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval925 (NO_SOURCE_FILE:1) |
| 335 | +user=> '(1 3 4) ; quote |
| 336 | +(1 3 4) |
| 337 | +user=> (quote (1 2 3)) ; using the longer quote method |
| 338 | +(1 2 3) |
| 339 | +user=> |
| 340 | +---- |
| 341 | + |
| 342 | +* http://clojure.org/special_forms#quote[Clojure Official Documentation] |
| 343 | + |
| 344 | +== ; - Comment |
| 345 | + |
| 346 | +`;` is a comment. In fact it's a comment *macro* that takes all input from its |
| 347 | +starting point to the end of the line and ensures that the reader ignores it. |
| 348 | +[source,clojure] |
| 349 | +---- |
| 350 | +user=> (def x "x") ; this is a comment |
| 351 | +#'user/x |
| 352 | +user=> ; this is a comment too |
| 353 | +<returns nothing> |
| 354 | +---- |
0 commit comments