branch: externals/taxy commit a7ff5573aead56286177038d2b37aba4489afc59 Author: Adam Porter <a...@alphapapa.net> Commit: Adam Porter <a...@alphapapa.net>
Docs: Document new things --- README.org | 113 ++++++++++++++++++++++++++++++++++++ taxy.el | 2 - taxy.info | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 270 insertions(+), 37 deletions(-) diff --git a/README.org b/README.org index 8eebebe..529f475 100644 --- a/README.org +++ b/README.org @@ -456,6 +456,7 @@ To return a taxy in a more human-readable format (with only relevant fields incl :CONTENTS: - [[#multi-level-dynamic-taxys][Multi-level dynamic taxys]] - [[#chains-of-independent-multi-level-dynamic-taxys]["Chains" of independent, multi-level dynamic taxys]] +- [[#defining-a-classification-domain-specific-language][Defining a classification domain-specific language]] :END: You may not always know in advance what taxonomy a set of objects fits into, so =taxy= lets you add taxys dynamically by using the ~:take~ function to add a taxy when an object is "taken into" a parent taxy's items. For example, you could dynamically classify buffers by their major mode like so: @@ -623,6 +624,118 @@ Now let's fill the taxy with the sports and format it: ("Soccer" "Tennis" "Football" "Baseball")))))) #+END_SRC +*** Defining a classification domain-specific language + +When writing a larger Taxy-based application, it may be necessary to define a number of key functions that would be unwieldy to manage in a ~cl-labels~ form. For this case, Taxy provides the macro ~taxy-define-key-definer~ to easily define Taxy key functions in an application library. Those functions are then passed to the function ~taxy-make-take-function~ at runtime, along with a list of keys being used to classify items. Using these allows key functions to be defined in top-level f [...] + +Extending the previous ~sporty~ example, let's redefine its key functions using ~taxy-define-key-definer~: + +#+begin_src elisp :exports code :results silent :lexical t + (taxy-define-key-definer sporty-define-key + sporty-keys "sporty" + "Define a `sporty' key function.") + + (sporty-define-key disc-based () + (if (member 'disc (sport-uses item)) + "Disc-based" + "Non-disc-based")) + + (sporty-define-key uses (&optional thing) + (pcase thing + (`nil (sport-uses item)) + (_ (when (cl-typecase (sport-uses item) + (symbol (equal thing (sport-uses item))) + (list (member thing (sport-uses item)))) + thing)))) + + (sporty-define-key venue (&optional place) + (pcase place + (`nil (sport-venue item)) + (_ (when (equal place (sport-venue item)) + (sport-venue item))))) +#+end_src + +Now we'll define the default keys to use when classifying items. This list is equivalent to the one passed to ~taxy-take-keyed~ in the previous, "Chains" example. + +#+begin_src elisp :exports code :results silent :lexical t + (defvar sporty-default-keys + '( + ((venue 'outdoor) + disc-based) + + ((venue 'indoor) + (uses 'ball) + (uses 'disc) + (uses 'glove) + (uses 'racket)))) +#+end_src + +Finally, rather than using a pre-made taxy struct, we make one at runtime, making the ~:take~ function with ~taxy-make-take-function~. + +#+begin_src elisp :exports both :results code :lexical t + (let ((taxy (make-taxy + :name "Sporty (DSL)" + :take (taxy-make-take-function sporty-default-keys + sporty-keys)))) + (thread-last taxy + (taxy-fill sports) + (taxy-mapcar #'sport-name) + taxy-plain)) +#+end_src + +Which gives us: + +#+RESULTS: +#+begin_src elisp + ("Sporty (DSL)" + ((indoor + ((ball + ("Volleyball" "Basketball") + ((glove + ("Handball")) + (racket + ("Racquetball")))))) + (outdoor + (("Disc-based" + ("Ultimate" "Disc golf")) + ("Non-disc-based" + ("Soccer" "Tennis" "Football" "Baseball")))))) +#+end_src + +As you can see, the result is the same as that in the previous example, but we've defined a kind of DSL for grouping sports in a modular, extendable way. + +This also allows the grouping keys to be easily changed at runtime, producing a different result. For example, we could group sports by, first, whether they use a ball, and then by venue. Let's do this in a function so that users can pass their own list of keys: + +#+begin_src elisp :exports both :results code :lexical t + (cl-defun sporty-classify (sports &key (keys sporty-default-keys)) + (declare (indent defun)) + (let* ((taxy (make-taxy + :name "Sporty (DSL)" + :take (taxy-make-take-function keys + sporty-keys)))) + (thread-last taxy + (taxy-fill sports) + (taxy-mapcar #'sport-name) + taxy-plain))) + + (sporty-classify sports + :keys '((uses 'ball) venue)) +#+end_src + +And this produces: + +#+RESULTS: +#+begin_src elisp + ("Sporty (DSL)" + ((outdoor + ("Ultimate" "Disc golf")) + (ball + ((indoor + ("Volleyball" "Handball" "Racquetball" "Basketball")) + (outdoor + ("Soccer" "Tennis" "Football" "Baseball")))))) +#+end_src + ** Reusable taxys Since taxys are structs, they may be stored in variables and used in other structs (being sure to copy the root taxy with ~taxy-emptied~ before filling). For example, this shows using =taxy= to classify Matrix rooms in [[https://github.com/alphapapa/ement.el][Ement.el]]: diff --git a/taxy.el b/taxy.el index 0bd2160..972c4e4 100644 --- a/taxy.el +++ b/taxy.el @@ -267,8 +267,6 @@ KEY is passed to `cl-sort', which see." ;; Utilities to define key and take functions in a standard way. -;; TODO: Document these. - (defmacro taxy-define-key-definer (name variable prefix docstring) "Define a macro NAME that defines a key-function-defining macro. The defined macro, having string DOCSTRING, associates the diff --git a/taxy.info b/taxy.info index 5699555..9f28f9e 100644 --- a/taxy.info +++ b/taxy.info @@ -60,6 +60,7 @@ Dynamic taxys * Multi-level dynamic taxys:: * "Chains" of independent, multi-level dynamic taxys: "Chains" of independent multi-level dynamic taxys. +* Defining a classification domain-specific language:: Changelog @@ -546,7 +547,7 @@ File: README.info, Node: Dynamic taxys, Next: Reusable taxys, Up: Usage 3.1 Dynamic taxys ================= - • • + • • • You may not always know in advance what taxonomy a set of objects fits into, so ‘taxy’ lets you add taxys dynamically by using the ‘:take’ function to add a taxy when an object is "taken into" a parent taxy’s @@ -605,6 +606,7 @@ and it produces this taxonomy of buffers: * Multi-level dynamic taxys:: * "Chains" of independent, multi-level dynamic taxys: "Chains" of independent multi-level dynamic taxys. +* Defining a classification domain-specific language:: File: README.info, Node: Multi-level dynamic taxys, Next: "Chains" of independent multi-level dynamic taxys, Up: Dynamic taxys @@ -662,7 +664,7 @@ directory. (#<buffer taxy-magit-section.el> #<buffer taxy.el<taxy.el> #<buffer scratch.el>)))))))) -File: README.info, Node: "Chains" of independent multi-level dynamic taxys, Prev: Multi-level dynamic taxys, Up: Dynamic taxys +File: README.info, Node: "Chains" of independent multi-level dynamic taxys, Next: Defining a classification domain-specific language, Prev: Multi-level dynamic taxys, Up: Dynamic taxys 3.1.2 "Chains" of independent, multi-level dynamic taxys -------------------------------------------------------- @@ -734,6 +736,125 @@ use: ("Soccer" "Tennis" "Football" "Baseball")))))) +File: README.info, Node: Defining a classification domain-specific language, Prev: "Chains" of independent multi-level dynamic taxys, Up: Dynamic taxys + +3.1.3 Defining a classification domain-specific language +-------------------------------------------------------- + +When writing a larger Taxy-based application, it may be necessary to +define a number of key functions that would be unwieldy to manage in a +‘cl-labels’ form. For this case, Taxy provides the macro +‘taxy-define-key-definer’ to easily define Taxy key functions in an +application library. Those functions are then passed to the function +‘taxy-make-take-function’ at runtime, along with a list of keys being +used to classify items. Using these allows key functions to be defined +in top-level forms, and it allows an application to be extended by +users’ defining additional key functions in their configuration. + + Extending the previous ‘sporty’ example, let’s redefine its key +functions using ‘taxy-define-key-definer’: + + (taxy-define-key-definer sporty-define-key + sporty-keys "sporty" + "Define a `sporty' key function.") + + (sporty-define-key disc-based () + (if (member 'disc (sport-uses item)) + "Disc-based" + "Non-disc-based")) + + (sporty-define-key uses (&optional thing) + (pcase thing + (`nil (sport-uses item)) + (_ (when (cl-typecase (sport-uses item) + (symbol (equal thing (sport-uses item))) + (list (member thing (sport-uses item)))) + thing)))) + + (sporty-define-key venue (&optional place) + (pcase place + (`nil (sport-venue item)) + (_ (when (equal place (sport-venue item)) + (sport-venue item))))) + + Now we’ll define the default keys to use when classifying items. +This list is equivalent to the one passed to ‘taxy-take-keyed’ in the +previous, "Chains" example. + + (defvar sporty-default-keys + '( + ((venue 'outdoor) + disc-based) + + ((venue 'indoor) + (uses 'ball) + (uses 'disc) + (uses 'glove) + (uses 'racket)))) + + Finally, rather than using a pre-made taxy struct, we make one at +runtime, making the ‘:take’ function with ‘taxy-make-take-function’. + + (let ((taxy (make-taxy + :name "Sporty (DSL)" + :take (taxy-make-take-function sporty-default-keys + sporty-keys)))) + (thread-last taxy + (taxy-fill sports) + (taxy-mapcar #'sport-name) + taxy-plain)) + + Which gives us: + + ("Sporty (DSL)" + ((indoor + ((ball + ("Volleyball" "Basketball") + ((glove + ("Handball")) + (racket + ("Racquetball")))))) + (outdoor + (("Disc-based" + ("Ultimate" "Disc golf")) + ("Non-disc-based" + ("Soccer" "Tennis" "Football" "Baseball")))))) + + As you can see, the result is the same as that in the previous +example, but we’ve defined a kind of DSL for grouping sports in a +modular, extendable way. + + This also allows the grouping keys to be easily changed at runtime, +producing a different result. For example, we could group sports by, +first, whether they use a ball, and then by venue. Let’s do this in a +function so that users can pass their own list of keys: + + (cl-defun sporty-classify (sports &key (keys sporty-default-keys)) + (declare (indent defun)) + (let* ((taxy (make-taxy + :name "Sporty (DSL)" + :take (taxy-make-take-function keys + sporty-keys)))) + (thread-last taxy + (taxy-fill sports) + (taxy-mapcar #'sport-name) + taxy-plain))) + + (sporty-classify sports + :keys '((uses 'ball) venue)) + + And this produces: + + ("Sporty (DSL)" + ((outdoor + ("Ultimate" "Disc golf")) + (ball + ((indoor + ("Volleyball" "Handball" "Racquetball" "Basketball")) + (outdoor + ("Soccer" "Tennis" "Football" "Baseball")))))) + + File: README.info, Node: Reusable taxys, Next: Threading macros, Prev: Dynamic taxys, Up: Usage 3.2 Reusable taxys @@ -1125,39 +1246,40 @@ GPLv3 Tag Table: Node: Top218 -Node: Examples1761 -Node: Numbery (starting basically)2080 -Node: Lettery (filling incrementally)7841 -Node: Sporty (understanding completely)10355 -Node: Applications16342 -Node: Installation16742 -Node: Usage17055 -Node: Dynamic taxys19192 -Node: Multi-level dynamic taxys21752 -Node: "Chains" of independent multi-level dynamic taxys23945 -Node: Reusable taxys26817 -Node: Threading macros30992 -Node: Modifying filled taxys31531 -Node: Magit section32349 -Node: Changelog33037 -Node: 06-pre33229 -Node: Additions33341 -Node: 0534179 -Node: Additions (1)34318 -Node: Fixes35424 -Node: 0435578 -Node: 0335800 -Node: Changes35929 -Node: Fixes (1)36292 -Node: 0236727 -Node: Changes (1)36896 -Node: Additions (2)37188 -Node: Fixes (2)38047 -Node: 0138301 -Node: Development38400 -Node: Copyright assignment38606 -Node: Credits39194 -Node: License39384 +Node: Examples1816 +Node: Numbery (starting basically)2135 +Node: Lettery (filling incrementally)7896 +Node: Sporty (understanding completely)10410 +Node: Applications16397 +Node: Installation16797 +Node: Usage17110 +Node: Dynamic taxys19247 +Node: Multi-level dynamic taxys21866 +Node: "Chains" of independent multi-level dynamic taxys24059 +Node: Defining a classification domain-specific language26990 +Node: Reusable taxys31152 +Node: Threading macros35327 +Node: Modifying filled taxys35866 +Node: Magit section36684 +Node: Changelog37372 +Node: 06-pre37564 +Node: Additions37676 +Node: 0538514 +Node: Additions (1)38653 +Node: Fixes39759 +Node: 0439913 +Node: 0340135 +Node: Changes40264 +Node: Fixes (1)40627 +Node: 0241062 +Node: Changes (1)41231 +Node: Additions (2)41523 +Node: Fixes (2)42382 +Node: 0142636 +Node: Development42735 +Node: Copyright assignment42941 +Node: Credits43529 +Node: License43719 End Tag Table