|
57 | 57 | Destructuring
|
58 | 58 | -------------
|
59 | 59 |
|
60 |
| -TBD |
| 60 | +The most common type of name binding encountered in Basilisp code is that of a single symbol to a value. |
| 61 | +For example, below the name ``a`` is bound to the result of the expression ``(+ 1 2)``:: |
| 62 | + |
| 63 | + (let [a (+ 1 2)] |
| 64 | + a) |
| 65 | + |
| 66 | +In many cases this form of name binding is sufficient. |
| 67 | +However, when dealing with data nested in vectors or maps of known shapes, it would be much more convenient to bind those values directly without needing to write collection accessor functions by hand. |
| 68 | +Basilisp supports a form of name binding known as destructuring, which allows convenient name binding of values from within sequential and associative data structures. |
| 69 | +Destructuring is supported everywhere names are bound: :lpy:form:`fn` argument vectors, :lpy:form:`let` bindings, and :lpy:form:`loop` bindings. |
| 70 | + |
| 71 | +.. note:: |
| 72 | + |
| 73 | + Names without a corresponding element in the data structure (typically due to absence) will bind to ``nil``. |
| 74 | + |
| 75 | +.. _sequential_destructuring: |
| 76 | + |
| 77 | +Sequential Destructuring |
| 78 | +^^^^^^^^^^^^^^^^^^^^^^^^ |
| 79 | + |
| 80 | +Sequential destructuring is used to bind values from sequential types. |
| 81 | +The binding form for sequential destructuring is a vector. |
| 82 | +Names in the vector will be bound to their corresponding indexed element in the sequential expression value, fetched from that type as by :lpy:fn:`nth`. |
| 83 | +As a result, any data type supported by ``nth`` natively supports sequential destructuring, including vectors, lists, strings, Python lists, and Python tuples. |
| 84 | +It is possible to collect the remaining unbound elements as a ``seq`` by providing a trailing name separated from the individual bindings by an ``&``. |
| 85 | +The rest element will be bound as by :lpy:fn:`nthnext`. |
| 86 | +It is also possible to bind the full collection to a name by adding a trailing ``:as`` name after all binding forms and optional rest binding. |
| 87 | + |
| 88 | +.. code-block:: |
| 89 | +
|
| 90 | + (let [[a b c & others :as coll] [:a :b :c :d :e :f]] |
| 91 | + [a b c others coll]) |
| 92 | + ;;=> [:a :b :c (:d :e :f) [:a :b :c :d :e :f]] |
| 93 | +
|
| 94 | +Sequential destructuring may also be nested: |
| 95 | + |
| 96 | +.. code-block:: |
| 97 | +
|
| 98 | + (let [[[a b c] & others :as coll] [[:a :b :c] :d :e :f]] |
| 99 | + [a b c others coll]) |
| 100 | + ;;=> [:a :b :c (:d :e :f) [[:a :b :c] :d :e :f]] |
| 101 | +
|
| 102 | +.. _associative_destructuring: |
| 103 | + |
| 104 | +Associative Destructuring |
| 105 | +^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 106 | + |
| 107 | +Associative destructuring is used to bind values from associative types. |
| 108 | +The binding form for associative destructuring is a map. |
| 109 | +Names in the map will be bound to their corresponding key in the associative expression value, fetched from that type as by :lpy:fn:`get`. |
| 110 | +Asd a result, any associative types supported by ``get`` natively supports sequential destructuring, including maps, vectors, strings, sets, and Python dicts. |
| 111 | +It is possible to bind the full collection to a name by adding an ``:as`` key. |
| 112 | +Default values can be provided for keys by providing a map of binding names to default values using the ``:or`` key. |
| 113 | + |
| 114 | +.. code-block:: |
| 115 | +
|
| 116 | + (defn f [{x :a y :b :as m :or {y 18}}] |
| 117 | + [x y m]) |
| 118 | +
|
| 119 | + (f {:a 1 :b 2}) ;;=> [1 2 {:a 1 :b 2}] |
| 120 | + (f {:a 1}) ;;=> [1 18 {:a 1}] |
| 121 | + (f {}) ;;=> [nil 18 {}] |
| 122 | +
|
| 123 | +For the common case where the names you intend to bind directly match the corresponding keyword name, you can use the ``:keys`` notation. |
| 124 | + |
| 125 | +.. code-block:: |
| 126 | +
|
| 127 | + (defn f [{:keys [a b] :as m}] |
| 128 | + [a b m]) |
| 129 | +
|
| 130 | + (f {:a 1 :b 2}) ;;=> [1 2 {:a 1 :b 2}] |
| 131 | + (f {:a 1}) ;;=> [1 nil {:a 1}] |
| 132 | + (f {}) ;;=> [nil nil {}] |
| 133 | +
|
| 134 | +There exists a corresponding construct for the symbol and string key cases as well: ``:syms`` and ``:strs``, respectively. |
| 135 | + |
| 136 | +.. code-block:: |
| 137 | +
|
| 138 | + (defn f [{:strs [a] :syms [b] :as m}] |
| 139 | + [a b m]) |
| 140 | +
|
| 141 | + (f {"a" 1 'b 2}) ;;=> [1 2 {"a" 1 'b 2}] |
| 142 | +
|
| 143 | +.. note:: |
| 144 | + |
| 145 | + The keys for the ``:strs`` construct must be convertible to valid Basilisp symbols. |
| 146 | + |
| 147 | +It is possible to bind namespaced keys directly using either namespaced individual keys or a namespaced version of ``:keys`` as ``:ns/keys``. |
| 148 | +Values will be bound to the symbol by their *name* only (as by :lpy:fn:`name`) -- the namespace is only used for lookup in the associative data structure. |
| 149 | + |
| 150 | +.. code-block:: |
| 151 | +
|
| 152 | + (let [{a :a b :a/b :c/keys [c d]} {:a "a" |
| 153 | + :b "b" |
| 154 | + :a/a "aa" |
| 155 | + :a/b "bb" |
| 156 | + :c/c "cc" |
| 157 | + :c/d "dd"}] |
| 158 | + [a b c d]) |
| 159 | + ;;=> ["a" "bb" "cc" "dd"] |
| 160 | +
|
| 161 | +.. _keyword_arguments: |
| 162 | + |
| 163 | +Keyword Arguments |
| 164 | +^^^^^^^^^^^^^^^^^ |
| 165 | + |
| 166 | +Basilisp functions can be defined with support for keyword arguments by defining the "rest" argument in an :lpy:fn:`defn` or :lpy:fn:`fn` form with associative destructuring. |
| 167 | +Callers can pass interleaved key/value pairs as positional arguments to the function and they will be collected into a single map argument which can be destructured. |
| 168 | +If a single trailing map argument is passed by callers (instead of or in addition to other key/value pairs), that value will be joined into the final map. |
| 169 | + |
| 170 | +.. code-block:: |
| 171 | +
|
| 172 | + (defn f [& {:keys [a b] :as kwargs}] |
| 173 | + [a b kwargs]) |
| 174 | +
|
| 175 | + (f :a 1 :b 2) ;;=> [1 2 {:a 1 :b 2}] |
| 176 | + (f :a 1 {:b 2}) ;;=> [1 2 {:a 1 :b 2}] |
| 177 | + (f {:a 1 :b 2}) ;;=> [1 2 {:a 1 :b 2}] |
| 178 | +
|
| 179 | +.. note:: |
| 180 | + |
| 181 | + Basilisp keyword arguments are distinct from Python keyword arguments. |
| 182 | + Basilisp functions can be :ref:`defined with Python compatible keyword arguments <basilisp_functions_with_kwargs>` but the style described here is intended primarily for Basilisp functions called only by other Basilisp functions. |
| 183 | + |
| 184 | +.. warning:: |
| 185 | + |
| 186 | + The trailing map passed to functions accepting keyword arguments will silently overwrite values passed positionally. |
| 187 | + Callers should take care when using the trailing map calling convention. |
| 188 | + |
| 189 | + .. code-block:: |
| 190 | +
|
| 191 | + (defn f [& {:keys [a b] :as kwargs}] |
| 192 | + [a b kwargs]) |
| 193 | +
|
| 194 | + (f :a 1 {:b 2 :a 3}) |
| 195 | + ;;=> [3 2 {:a 3 :b 2}] |
| 196 | +
|
| 197 | +.. _nested_destructuring: |
| 198 | + |
| 199 | +Nested Destructuring |
| 200 | +^^^^^^^^^^^^^^^^^^^^ |
| 201 | + |
| 202 | +Both associative and sequential destructuring binding forms may be nested within one another. |
| 203 | + |
| 204 | +.. code-block:: |
| 205 | +
|
| 206 | + (let [[{:keys [a] [e f] :d} [b c]] [{:a 1 :d [4 5]} [:b :c]]] |
| 207 | + [a b c e f]) |
| 208 | + ;;=> [1 :b :c 4 5] |
61 | 209 |
|
62 | 210 | .. _references_and_refs:
|
63 | 211 |
|
|
71 | 219 | Transducers
|
72 | 220 | -----------
|
73 | 221 |
|
| 222 | +TBD |
| 223 | + |
74 | 224 | .. _hierarchies:
|
75 | 225 |
|
76 | 226 | Hierarchies
|
|
0 commit comments