npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

monadic

v0.0.5

Published

Do-notation and Monads for JavaScript

Downloads

21

Readme

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.