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:

Common Lisp
(in-package :mora)

Lisp is the working language

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.

Defining a pattern class

defpattern defines a named pattern class, a constructor, and observation and query methods:

Common Lisp
(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:

Common Lisp
(defparameter *live-pulse* (pulse :factor 1))

(d1! *live-pulse*)

The body is evaluated on each query

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.

Changing behavior with a method

An applicable method can change the behavior of the same object on subsequent queries. This method specializes events-in for pulse-pattern:

Common Lisp
(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.

Custom methods own their determinism

Mora’s supplied chance operators are temporally reproducible. Custom pattern methods remain responsible for their own deterministic behavior.

Defining a transformation

deftransform defines a reusable, inspectable transformation:

Common Lisp
(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.

What still needs registration

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.

Inspecting what you defined

The following are intentionally non-audible inspection forms:

Common Lisp
(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

Documentation

Search the documentation

Results update as you type. Use the up and down arrow keys to choose a result and Enter to open it.