Guide
Extending Mora
Define pattern classes with defpattern and transformations with deftransform, and specialize protocol methods that change how existing objects answer future queries.
Mora’s vocabulary stays open in the running image. This guide shows how to define a new pattern class and a new transformation, how a method can change the behavior of an object that is already playing, and what such changes do and do not affect.
The examples assume the public package:
(in-package :mora)Mora’s terse pattern notation is embedded in Common Lisp. Ordinary functions, macros, classes, generic functions, and the inspector are available when a composition needs to become a new instrument or language feature.
defpattern and deftransform provide concise extension routes; they are not the only routes. Extending the system can mean defining another pattern class or specializing a protocol method, rather than adding another case to a central list of recognized types. The reasoning behind that design is in Common Lisp and CLOS.
defpattern defines a named pattern class, a constructor, and observation and query methods:
(defpattern pulse (&key (factor 1))
"An inspectable backbeat. Example: (pulse :factor 2)."
(fast factor (s "bd ~ sn ~")))The generated class is pulse-pattern, with a pulse-factor reader. The constructor creates an instance that can be held in a variable and assigned to a channel:
(defparameter *live-pulse* (pulse :factor 1))
(d1! *live-pulse*)The defpattern body is evaluated by its query method, not once as a universal compile step. Keep the body suitable for repeated interpretation over arbitrary spans. Ordinary I/O or resource creation in the body would be ordinary Lisp side effects on each query.
The generated structural observation also evaluates the body, to expose the resulting child.
An applicable method can change the behavior of the same object on subsequent queries. This method specializes events-in for pulse-pattern:
(defmethod events-in ((self pulse-pattern) query-span)
(declare (ignore self))
(events-in (s "bd hh sn [hh cp]") query-span))After this definition, *live-pulse*, still assigned to d1, answers future queries with the new phrase. Adding or redefining applicable protocol methods in this way is intercession; What remains after evaluation describes it alongside introspection.
Mora’s supplied chance operators are temporally reproducible. Custom pattern methods remain responsible for their own deterministic behavior.
deftransform defines a reusable, inspectable transformation:
(deftransform doubles (factor)
"Double or otherwise scale source rate."
(:apply (pattern)
(fast factor pattern)))
(d1! (>> (s "bd sn")
(doubles 2)))transform-summary and transform-parameters expose the new transformation.
A new class or method does not require editing a central dispatcher for those protocol routes. Surface integration, point-free normalization, and editor signature coverage may still have their own registration or harvesting requirements.
The following are intentionally non-audible inspection forms:
(defparameter *pattern* (>> (s "bd ~ sn hh") (fast 2)))
(class-of *pattern*)
(pattern-summary *pattern*)
(pattern-children *pattern*)
(pattern-metadata *pattern*)
(events-in *pattern* (span 13/4 7/2))They are the basis for the retained-object design explained in What remains after evaluation.
Source references
Repository-relative paths in the Mora source where these mechanisms are implemented.
-
src/mini/defpattern.lisp -
src/mini/deftransform.lisp -
src/core/pattern.lisp -
src/mini/transforms.lisp -
src/mini/clauses.lisp