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

burrido

v1.0.8

Published

Monadic do-notation for JavaScript

Downloads

273

Readme

burrido Build Status

An experiment in bringing Haskell's programmable semicolon to JavaScript, using generators.

Installation

npm install --save burrido

Usage

import Monad from 'burrido'

const ArrayMonad = Monad({
  pure: (x) => [x],
  bind: (xs, f) => xs.map(f).reduce((a, b) => a.concat(b), [])
})

ArrayMonad.Do(function*() {
  const x = yield [1,2]
  const y = yield [3,4]
  return x * y
}) // -> [3,4,6,8]

The above should look fairly self-explanatory to a Haskell programmer: we are declaring a Monad instance for arrays, which requires us to define two functions: pure and bind. Then we obtain a special function Do which is a do-notation tailored to that particular monad. We pass a generator function to Do, within which we gain access to the yield keyword, allowing us to "unwrap" monadic values and release their effects.

In fact this is a bit more versatile than Haskell's do-notation in a couple of interesting ways:

  1. Haskell's Monad is a type class, which means that there can only be one way in which a given type constructor is considered a monad within a given scope. But some type constructors can be considered monadic in more than one way (e.g. Either). By contrast, here you can create as many Monad definitions as you want for a particular type (constructor), and each just has its own special Do function.
  2. While
const foo = yield bar

is comparable to

foo <- bar

in do-notation, one can also create compound yield expressions which have no direct analogue in Haskell. For example,

const foo = yield (yield bar)

would have to be written as

foo' <- bar
foo <- foo'

in do-notation. In the context of Do blocks, yield serves a similar purpose to the ! operator in both Idris and the Effectful library for Scala.

An example using RxJS

RxJS Observables form a monad in several different ways:

const { just: pure } = Observable

const { Do: doConcat } = Monad({
  pure,
  bind: (x, f) => x.concatMap(f)
})

const { Do: doMerge } = Monad({
  pure,
  bind: (x, f) => x.flatMap(f)
})

const { Do: doLatest } = Monad({
  pure,
  bind: (x, f) => x.flatMapLatest(f)
})

It's insructive to see what happens when you apply these different do-notations to the same generator block:

const { from } = Observable

const block = function*() {
  // for each x in [1,2,3]...
  const x = yield from([1,2,3])
  // wait 1 second
  yield pure({}).delay(1000)
  // then return the value
  return x
}

// Prints 1, 2, and 3 separated by 1 second intervals
doConcat(block).subscribe(console.log)
// Waits 1 second and then prints 1, 2, 3 all at once
doMerge(block).subscribe(console.log)
// Waits 1 second and then prints 3
doLatest(block).subscribe(console.log)

This should make sense if you think about the semantics of each of these different methods of "flattening" nested Observables. Each do* flavor applies its own semantics to the provided block, but they all return Observables, so we can freely combine them:

doConcat(function*() {
  const x = yield doConcat(function*() {
          //...
        }),
        y = yield doMerge(function*() {
          //...
        }),
        z = yield doLatest(function*() {
          //...
        })
  return { x, y, z }
})

RxJS has a function spawn which allows you to use this kind of syntax with Observables, but it only works properly with single-valued streams (essentially Promises), whereas burrido allows manipulating streams of multiple values, using multiple different semantics.