Add descriptions and examples for a bunch of macros.

Add defpathedfn, defprotocolpath, extend-protocolpath, fixed-pathed-nav,
variable-pathed-nav, paramscollector, pathed-collector, and paramsfn.
This commit is contained in:
Chris O'Donnell 2016-06-12 22:49:19 -04:00
parent 63934b022e
commit ad19789422

View file

@ -19,12 +19,138 @@ Declares a new symbol available to be defined as a path. If the path will requir
(0 0 2 3 4)
```
### defpathedfn
`(defpathedfn name & args)`
This is sugar to add [:pathedfn true] to the metadata map of the function. The syntax is the same as `defn` (optional docstring, etc.). Often used with [fixed-pathed-nav](#fixed-pathed-nav) or [variable-pathed-nav](#variable-pathed-nav) to enable late binding parameters. Non-path arguments should be preceded by ^:notpath.
```clojure
=> (defpathedfn walk-pred
"Walks to integers satisfying apred."
[^:notpath apred]
(walker #(and (integer? %) (apred %))))
=> (select (walk-pred even?) [1 [2] [[[3 4]]] [5 [[6]] 7 8]])
(2 4 6 8)
=> (transform (walk-pred even?) #(/ % 2) [1 2 [3] [[[4]]] [5 6] [[7 8]]])
[1 1 [3] [[[2]]] [5 3] [[7 4]]]
;; The implementation of transformed
(defpathedfn transformed
"Navigates to a view of the current value by transforming it with the
specified path and update-fn.
The input path may be parameterized, in which case the result of transformed
will be parameterized in the order of which the parameterized navigators
were declared."
[path ^:notpath update-fn]
;; Bind the passed in path to late
;; Returns a navigator with the given select* and transform* implementations
(fixed-pathed-nav [late path]
(select* [this structure next-fn]
(next-fn (compiled-transform late update-fn structure)))
(transform* [this structure next-fn]
(next-fn (compiled-transform late update-fn structure)))))
```
### defprotocolpath
`(defprotocolpath name)`
`(defprotocolpath name params)`
Defines a navigator that chooses the path to take based on the type
of the value at the current point. May be specified with parameters to
specify that all extensions must require that number of parameters.
Currently not available for ClojureScript.
```clojure
=> (defrecord SingleAccount [funds])
=> (defrecord FamilyAccount [single-accounts])
=> (defprotocolpath FundsPath)
=> (extend-protocolpath FundsPath
SingleAccount :funds
FamilyAccount [:single-accounts ALL FundsPath])
=> (select [ALL FundsPath]
[(->SingleAccount 100) (->SingleAccount 3)
(->FamilyAccount [(->SingleAccount 15) (->SingleAccount 12)])])
[100 3 15 12]
```
### extend-protocolpath
`(extend-protocolpath protpath & extensions)`
Extends a protocol path `protpath` to a list of types. The `extensions` argument has the form `type1 path1 type2 path2...`.
See [defprotocolpath](#defprotocolpath) for an example.
### fixed-pathed-nav
`(fixed-pathed-nav bindings select-impl transform-impl)`
`(fixed-pathed-nav bindings transform-impl select-impl)`
The first form is canonical.
This helper is used to define navigators that take in a fixed number of other
paths as input. Those paths may require late-bound params, so this helper
will create a parameterized navigator if that is the case. If no late-bound params
are required, then the result is executable.
`bindings` must be of the form `[path-binding1 path1 path-binding2 path2...]`.
`select-impl` must be of the form `(select* [this structure next-fn] body)`. It should return the result of calling `next-fn` on whatever transformation the navigator applies to `structure`.
`transform-impl` must be of the form `(transform* [this structure next-fn] body)`. It should find the result of calling `nextfn` on whatever transformation the navigator applies to `structure`. Then it should return the result of reconstructing the original structure using the results of the `nextfn` call.
See [defpathedfn](#defpathedfn) for an example.
### providepath
`(providepath name apath)`
Defines the path that will be associated to the provided name. The name must have been previously declared using [declarepath](#declarepath).
### variable-pathed-nav
`(variable-pathed-nav [paths-binding paths-seq] select-impl transform-impl)`
`(variable-pathed-nav [paths-binding paths-seq] transform-impl select-impl)`
The first form is canonical.
This helper is used to define navigators that take in a variable number of other
paths as input. Those paths may require late-bound params, so this helper
will create a parameterized navigator if that is the case. If no late-bound params
are required, then the result is executable.
Binds the passed in seq of paths to `paths-binding`, which can be used in `select-impl` and `transform-impl`.
The implementation of `multi-path` is a nice example of the use of `variable-pathed-nav`.
```clojure
=> (defpathedfn multi-path
[& paths]
(variable-pathed-nav [compiled-paths paths]
(select* [this structure next-fn]
(->> compiled-paths
;; seq with the results of navigating each passed in path
(mapcat #(compiled-select % structure))
;; pass each result to the following navigator
(mapcat next-fn)
doall))
(transform* [this structure next-fn]
;; apply the transform to each passed in path in order
(reduce
(fn [structure path]
(compiled-transform path next-fn structure))
structure
compiled-paths))))
```
## Collector Macros
### defcollector
@ -47,6 +173,41 @@ An informative example is the actual implementation of `putval`, which follows.
(3 4 5 6 7)
```
### paramscollector
`(paramscollector params collect-val-impl)`
Defines a Collector with late bound parameters. This collector can be precompiled
with other selectors without knowing the parameters. When precompiled with other
selectors, the resulting selector takes in parameters for all selectors in the path
that needed parameters (in the order in which they were declared).
Returns an "anonymous Collector." See [defcollector](#defcollector).
### pathed-collector
`(pathed-collector [name path] collect-val-impl)`
This helper is used to define collectors that take in a single selector
paths as input. That path may require late-bound params, so this helper
will create a parameterized selector if that is the case. If no late-bound params
are required, then the result is executable.
Binds the passed in path to `name`.
`collect-val-impl` must be of the form `(collect-val [this structure] body)`. It should return the value to be collected.
The implementation of `collect` is a good example of how `pathed-collector` can be used.
```clojure
=> (defpathedfn
collect
[& path]
(pathed-collector [late path]
(collect-val [this structure]
(compiled-select late structure))))
```
## Navigator Macros
### defnav
@ -94,3 +255,15 @@ See also [nav](#nav)
`(nav params transform-impl select-impl)`
Returns an "anonymous navigator." See [defnav](#defnav).
### paramsfn
`(paramsfn params [structure-binding] impl)`
Helper macro for defining filter functions with late binding parameters.
```clojure
=> (let [c-path (comp-paths (paramsfn [val] [x] (< x val)))]
(select [ALL (c-path 5)] [2 7 3 4 10 8]))
[2 3 4]
```