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

impure

v1.0.0

Published

A container for impure code.

Downloads

4

Readme

impure Build Status codecov

A wrapper object for non-deterministic code.

Pure functions are deterministic. They take in values and computes the result based solely on these values. They are predictable. They have no side-effects. They are referentially-transparent. They are easier to reason. They are easier to test.

Haskell enforces purity at type level, by clearly separating IO functions apart from pure functions. IO functions can call pure functions and produce side-effects, but pure functions cannot produce any side-effect.

In JavaScript, there is no such distinction. Any function may freely call any other function. And many times, when not used carefully, this can produce unintended side-effects.

By wrapping all non-deterministic code inside a wrapper, all side-effects must be invoked through a runner. If your function is not wrapped in IO, it cannot cause any side-effect to happen.

Example

An IO function [(...args) → IO]

Here, we’re wrapping console.log() inside an IO wrapper.

// example/console.js
import { createIO } from '..'

export const log = (...args) => createIO(({ console }) => {
  console.log(...args)
})

It does not access any global variable. Instead, the console object is injected into the IO.

A pure function

Here’s a function that simply adds two values, just for example.

You can see that even though it can import console.js and call log(), it will not produce any side-effect. You only receive a wrapper, and the only way to make side-effect happen is to trigger it explicitly (through the run function).

Since this function is not given the run function, it cannot cause any IO operation to perform. So basically if we ban all impure function calls (e.g. banning access to console, Math.random, Date.now, etc) we end up with a truly pure code in this module.

// example/add.js
export const add = (a, b) => a + b

Another IO function

Here’s our main function. It runs our pure function and invoke our first IO function to log the result.

// example/main.js
import { add } from './add'
import { log } from './console'
import { createIO } from '..'

export const main = () => createIO((context, run) => {
  const result = add(30, 12)
  run(log('The result is ' + result))
})

Entry point

To actually run a wrapped IO object, you need to create the run function. For example, here’s how one might create an application’s entry point.

// example/index.js
import { createRun } from '..'
import { main } from './main'

const context = { console }
const run = createRun({ context })
run(main())

Testing code with IO

This actually makes our code testable — as we can create a fake console and use it in our test.

// example/test-utils/createFakeConsole.js
export function createFakeConsole () {
  const logged = [ ]
  return {
    log: (text) => {
      logged.push(text)
    },
    isLogged: (text) => {
      return logged.some(loggedText => text === loggedText)
    }
  }
}
// example/main.test.js
import { createFakeConsole } from './test-utils/createFakeConsole'
import { createRun } from '..'
import { main } from './main'

it('should log the result to console', () => {
  const context = { console: createFakeConsole() }
  const run = createRun({ context })
  run(main())
  assert(context.console.isLogged('The result is 42'))
})

the implementation

// createIO.js
import runInContext from './_runInContext'

export function createIO (operation) {
  return {
    [runInContext] (context, run) {
      return operation(context, run)
    }
  }
}
// createRun.js
import runInContext from './_runInContext'

export function createRun ({ context }) {
  return run

  function run (io) {
    return io[runInContext](context, run)
  }
}
// _runInContext.js
export default '(╯°□°)╯︵ ┻━┻'
// index.js
export { createIO } from './createIO'
export { createRun } from './createRun'