Modules
You can write nodejs style modules in Elfenben and require them in Elfenben or JavaScript.
mymodule.elf
===========
(set module.exports
(function (x) (* x x)))
console.log(square(2))
</pre>
<pre>
main.js
=======
require("elfenben/lib/require")
var square = require("./mymodule")
Monads
Monads allow you to process data in steps. There are different types of monads, and each type provides you with additional processing rules for each step. Fortunately you don't have to understand the workings of monads to use them. Elfenben comes with four builtin monad types. The "identity monad", "maybe monad", "array monad" and "state monad". You can also write your own monads in Elfenben.
;; using identity monad
(doMonad identityMonad
(a 1
b (* a 2))
(+ a b))
The expression above will return number 3. In general the domonad expression is written like below.
(domonad name (varname1 expression1 varname2 expression2 ...) (result expression))
Note that the variable a in step 1 is visible in step 2, and both a and b are visible in the result expression.
;; using maybe monad
(doMonad maybeMonad
(a (task1)
b (task2))
(doSomething))
The maybe monad is called so because may be it will do something. The way it works is that if any of the steps returns null it will return null and not carry out the rest os the steps.
The array monad is a array comprehension.
;; using array monad
(doMonad arrayMonad
(a [1,2,3]
b [3,4,5])
(+ a b))
;; ==> [ 4, 5, 6, 5, 6, 7, 6, 7, 8 ]
;; we only want results that are <= 6 below
(doMonad arrayMonad
(a [1,2,3]
b [3,4,5])
(when (<= (+ a b) 6)
(+ a b)))
;; ==> [ 4, 5, 6, 5, 6, 6 ]
(doMonad arrayMonad
(letters ["a", "b", "c"]
numbers [3, 4, 5])
[letters, numbers])
;; ==> [['a',3],['a',4],['a',5],['b',3],['b',4],['b',5],['c',3],['c',4],['c',5]]
The bindings for a
and b
are made for every element of the corresponding arrays. In the second example we applied a when condition to the result expression to keep out results greater than six. This when condition can only be used with the array monad and maybe monad. Or to be more specific, monads that define mZero
The state monad allows you to pass the current state through a set of steps. The state could be anything, an integer, string, array, object etc. In the example below we will maintain state of an immutable stack through a set of steps.
;; First we write two functions push and pop,
;; which will be the operations we carry out.
;; push is a monadic function that accepts an element and returns a function.
;; The returned function accepts the current state (stack array),
;; creates a new state by creating a new array with the element
;; and concats the previous state to it. (we want an immutable state)
;; and returns a monadic value which is and array of two elements. The result
;; of the current operation (undefined) in this case and the new state.
(var push
(function (element)
(function (state)
(var newstate [element])
(array undefined (newstate.concat state)))))
;; In the same way above we write a pop function that will remove one
;; element from the stack and creates a new stack
;; and returns the popped value and the new stack.
(var pop
(function ()
(function (state)
(var value (get 0 state))
(var newstate (state.slice 1))
(array value newstate))))
;; In the case of state monad domonad returns a function which we must call
;; with the initial state. We call the returned function stackOperation.
(var stackOperations
(doMonad stateMonad
(a (push 5)
b (push 10)
c (push 20)
d (pop))
d))
(stackOperations [])
;; ==> [ 20, [ 10, 5 ] ]
Writing your own monad
To write monads use the monad expression. It takes two arguments. The name for the monad, and an object of key value pairs. The keys mResult and mBind are obligatory. The values must be the corresponding monadic result function and the monadic bind function. Optionally you can have the mZero and mPlus keys with their corresponding values. The example below is the definition of the identity monad.
(monad identityMonad
(object
"mBind" (function (mv mf) (mf mv))
"mResult" (function (v) v)))
Elfenben also supports the withMonad expression.
(withMonad name (expression1) (expression2) ... )