Multimethods that combine rather than override inherited behavior
Clojure
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src/intentions
test/intentions
.gitignore
.travis.yml
README.md
project.clj

README.md

Intentions

Build Status

Intentions are a tool for runtime polymorphism in Clojure and ClojureScript. They behave much same as multimethods, with one key exception: where multimethods override inherited behavior, intentions combine it.

Installation

Add the following dependency to your project:

[intentions "0.2.1"]

Usage

An intention is created with defintent:

(defintent valid?
  :dispatch :type
  :combine #(and %1 %2))

An intention needs both a dispatch function, and a combine function. The combine function combines two return values into one. In this case, we perform a logical AND on the values.

Once an intention is stated, behavior can be added using conducts. These are analogous to methods:

(derive ::square ::quad)

(defconduct valid? ::quad [shape]
  (= (count (:sides shape)) 4))

(defconduct valid? ::square [shape]
  (apply = (:sides shape))

Unlike methods, conducts with derived dispatch values combine the functionality of their parents. Because ::square derives from ::quad, both conducts are applied, then combined using and:

(valid? {:type ::square, :sides [2 2 2 2]})
-> true

(valid? {:type ::square, :sides [2 2 2]})
-> false

Conducts are combined in a fixed order down the inheritance tree, with parents evaluated before children. So in the above case, the conduct for ::quad is evaluated before ::square. This ordering is particularly useful when using a combining function like merge.

When the inheritance order is ambiguous (such as in the case of a diamond dependency), dependencies are converted to strings and ordered alphanumerically, so as to always provide a consistent ordering. This can be overridden by using the prefer-conduct function.

For example:

;; Diamond dependency graph
(derive ::b ::a)
(derive ::c ::a)
(derive ::d ::b)
(derive ::d ::a)

(defintent order-example
  :dispatch identity
  :combine  concat)

(defconduct ::b [_] '(b))
(defconduct ::c [_] '(c))

If we were to dispatch on ::d, the order in which to apply ::b and ::c is ambiguous, so we default to alphanumeric ordering:

(order-example ::d)
-> (b c)

To explicit specify an ordering, use prefer-conduct:

(prefer-conduct order-example ::c ::b)
(order-example ::d)
-> (c b)

Further Documentation

License

Copyright © 2016 James Reeves

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.