New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

monadic

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

monadic

Do-notation and Monads for JavaScript

0.0.5
latest
Source
npm
Version published
Weekly downloads
1
-50%
Maintainers
1
Weekly downloads
 
Created
Source

Monadic

Introduction

Monadic:

  • adds Haskell-style do-notation to JavaScript;
  • provides a simple means to add custom monad implementations;
  • provides several monads implementations itself, including identity, maybe, list, stateT and contT;
  • through use of the list monad, you get list comprehensions in JavaScript!
  • through use of the contT monad transformer (which supports call/cc) offers a means of escape from the JavaScript Continuation Passing Style code spaghetti.

Do-notation

Write JavaScript files as normal, but use the extension .mjs rather than .js. In NodeJS, ensure you require monadic first, and NodeJS will then be able to load .mjs files.

In .mjs files you have everything you have in .js files, plus a do-block that looks like:

do monadObj {
    // ... do-notation expressions in here
};
  • where the monadObj is any object that supports mreturn and mbind methods.

  • Inside the do-block:

    • You have binding expressions such as a <- foo();
    • return invokes monadObj.mreturn;
    • You have an expression language which supports if (which must include an else branch) and function calls;
    • Using the var keyword is illegal;
    • Using while, for, for-in and do-while constructs are illegal: use recursion for looping within a monad, or exit the monad.
  • It is common practice to use return to lift any function call to any side-effecting operation.

Monads

var monadic = require('monadic');
var identityM = monadic.identity();
var maybeM = monadic.maybe();
var listM = monadic.list();
var stateTM = monadic.stateT(identityM);
var contTM = monadic.contT(identityM);

Obviously, for the transformers, the underlying monad need not be identity.

Some monads have additional methods. I recommend you check their source, but essentially all are modelled on their Haskell brethren with very minor adjustments. contT is likely to be the most interesting given its support for call/cc.

Utilities

var monadic = require('monadic');
var resultM = monadic.sequence(listOfMonads); // :: (Monad m) => [m a] -> m [a]

ContT and call/cc

NodeJS suffers from horrible CPS spaghetti. This can be solved using the contT monad transformer and call/cc. For example, inserting into mongodb:

function insertIntoCollection(collection, myObject) {
    var monadic = require('monadic');
    var when = require('when');
    var cont = monadic.contT(monadic.identity());
    var deferred = when.defer();
    cont.run(
        do cont {
            error <- cont.suspend(function (errK) {
                cont.run(do cont {
                    inserted <- cont.suspend(function (succK) {
                        collection.insert(myObject, {safe: true},
                            cont.errorSuccessCallback(errK, succK));
                    });
                    // Normally, the following would run before
                    // the insertion had finished, but not here!
                    return console.log('The insert succeeded:', inserted);
                    return deferred.resolver.resolve(inserted);
                });
            });
            // These are only run if errK is invoked.
            return console.log('The insert failed:', error);
            return deferred.resolver.reject(error);
        });
    return deferred.promise;
}

Despite some I/O going on, the expressions actually do get evaluated in the order you would expect if there was blocking I/O available. However, the I/O is still truly async which is why the function returns a promise (the function will complete before the insertion completes as normal for NodeJS). This promise will be resolved or rejected as appropriate on the result of the insertion to the collection.

Note the use of return to lift side-effecting operations into the monad.

Debugging

To see the transformed result when loading a .mjs module, do:

require.extensions['.mjs'].debugging = true;

The result of the transformation will be dumped via console.info as the module is loaded.

Browser Use

This is perfectly possible, but you must do the syntactic transformation yourself. The transformer can be used as:

var monadic = require('monadic');
var plainJSText = monadic.transform('/path/to/myModule.mjs');

Thus you can either build a server-side compile step, or a dynamic on-demand transformation if your server is NodeJS, or you can attempt to do the transformation in the browser and then eval the result.

Keywords

monads

FAQs

Package last updated on 16 Aug 2012

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts