Permacode is a Clojure library and Leiningen plug-in that allows developers to write purely declarative Clojure code that will allways run the same way.
Please see our documentations for more details.
There are three use cases for permacode, which are not mutual exclusive:
- A developer developing a permacode module.
- A developer using a specific version of a permacode module as a dependency of a Clojure project (which may or may not be a permacode module).
- A developer receiving a fully-qualified name of (typically) a function, evaluating it.
The following subsections refer to these use cases.
To make your project a permacode project, add permacode
as both a dependency and a leiningen plug-in
in the dev
profile.
When writing your code you're restricted to only use pure functions that are white-listed.
To see an up-to-date list of the functions white-listed in clojure.core
see the definition of core-white-list
in this source file.
There is also a small number of other namespaces (e.g., clojure.string
, clojure.set
) that are also white-listed
(see white-listed-ns
in the same file), where all functions are considered pure and are therefore allowed for use
Each source file must begin with an ns
expression, which may only contain :require
clauses.
See here for the exact details.
The rest of the expressions must be wrapped in premacode.core/pure
.
This is a macro that checks the underlying code (at compile time) for use of unauthorized symbols.
It results in the underlying code, so there is no run-time performance hit.
Source files can :require
white-listed libraris, permacode hashed libraries (see below) and one another.
Other dependencies are not allowed.
Testing can be done using any kind of testing library, such as clojure.test
or midje.
Once you're happy with the code, you can use lein permacode publish
to hash it.
publish
will serialize the source code and store it in a repository.
Currently only local repositories are supported. The default location is ~/.permacode
, but it can be modified by
setting the :permacode-repo
key in the project.clj
file.
publish
prints the hash codes produced for each module:
cloudlog.clj$ lein permacode publish
perm.QmYWHz6bjnvgiutyjNCRv8CAruCLFnJWuAA44Fos6pPj6z cloudlog.interset
perm.QmQodG29316hRLwuJVGpCP3CW7VCJEE5KTpK6ABDBDUT1H cloudlog.unify
perm.QmYX8EX8VtsAUhv9j2svB6m6KRTDaYb7NZn4AH73ifkzAA cloudlog.core
Whether or not your project is a permacode project (i.e., you intend to use lein permacode publish
on it),
you can :require
permacode modules by requiring a specific version. For example:
(ns perm-example.core
(:require [perm.QmYX8EX8VtsAUhv9j2svB6m6KRTDaYb7NZn4AH73ifkzAA :as cloudlog]
[permacode.core]))
To make this namespace available, use lein permacode deps
.
It will retrieve the dependency and place it in a file in your source directory tree.
For example, if your source directory is ./src
, lein permacode deps
will create
a directory ./src/perm
and place QmYX8EX8VtsAUhv9j2svB6m6KRTDaYb7NZn4AH73ifkzAA.clj
inside it.
As a best practice it is advised to add this directory to .gitignore
:
src/perm
Please note that for this to work the modules need to be in the repository.
The ultimate goal of permacode is to make it easy to distribute code.
Distributing code can be done by publish
ing it, and then by providing someone a fully-qualified symbol
identifying the code you wish them to run.
This symbol is typically a function, which is guaranteed to always run the same way.
The function eval-symbol takes a fully qualified symbol and evaluates it. Please consult the documentation to see how to use this function.
Please see here.