Liking cljdoc? Tell your friends :D

Morph

Morph is a library of Haskell-style morphisms: monoids, functors, and monads. These constructs are helpful for designing programs that are purely functional and that encapsulate the boilerplate employed by many programming techniques.

Features

  • Implementation based on protocols and data types.
  • Predefined monoids and functors; with support for Clojure collections.
  • Monads: Identity, Maybe, Either, Reader, Writer, State, Imperative.
  • Monad Transformers: MaybeT, EitherT, ReaderT, WriterT, StateT.
  • Support for curried functions.
  • Library of generic functions for the above constructs.
  • Sample code in src/main/resources.

These constructs have a reputation of being hard to explain and even harder to understand and to apply in everyday programming. I've made every effort to present them as regular techniques and idioms with practical benefits. Behind their strange or fancy names, these are just functions that work on data types.

An intuition of their behavior is all that's needed to take advantage of these functions; you may never need or want to write your own. I'm pleased with the power and simplicity these techniques have to offer and I hope you may find them useful as well.

Sample Usage

A simple expression evaluator uses the State monad; it can declare variables and clear the symbol table. Access to the symbol table is direct as if it were global, though all functions are pure.

(use '[blancas.morph core monads])

(def table {'DEG 57.295779 'E 2.718281 'PI 3.141592})

(declare run)

(defn calc
  "Evaluates and unboxes the operands; then applies the
   operator and returns the result as new state value."
  [op x y]
  (monad [a (run x) b (run y)]
    (state (op a b))))

(defn const
  "Returns a state value with a looked-up value or the given number."
  [x] (if (symbol? x) (gets x) (state x)))

(defn decl
  "Declares a variable as (var = val); value is used in-place."
  [x y] (>> (modify-state assoc x y) (state y)))

(defn clear
  "Clears the symbol table as (val %); value is used in-place."
  [x] (>> (put-state {}) (state x)))

(defn run
  "Dispatches on an expression or a single value."
  [op]
  (if (list? op)
    (case (second op)
      + (calc + (first op) (last op))
      - (calc - (first op) (last op))
      * (calc * (first op) (last op))
      / (calc / (first op) (last op))
      = (decl   (first op) (last op))
      % (clear  (first op)))
    (const op)))

Now we evaluate some expressions with table as the initial state.

(eval-state (run '((9 / 3) + (2 * (PI - E)))) table)
;; 3.846622
(exec-state (run '((180 / (k = 30)) + (k * (PI - E)))) table)
;; {PI 3.141592, E 2.718281, DEG 57.295779, k 30}
(exec-state (run '(((180 %) / (k = 30)) + ((j = 5) * (k - j)))) table)
;; {j 5, k 30}

Setup

Leiningen:

[org.blancas/morph "0.3.0"]

Maven:

<dependency>
  <groupId>org.blancas</groupId>
  <artifactId>morph</artifactId>
  <version>0.3.0</version>
</dependency>

Changes for release 0.3.0:

  • Fixed slow, reflective calls by adding type hints.

Documentation

Browse the whole change log.

Morph is documented in the Wiki.

Browse the Codox Morph v0.3.0 API.

To generate the API docs (in the codox directory):

lein doc

License

Copyright © 2013 Armando Blancas.

Licensed under the Eclipse Public License.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close