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

genfun

v5.0.0

Published

Fast, prototype-friendly multimethods.

Downloads

2,642,069

Readme

Genfun Travis npm npm

genfun is a Javascript library that lets you define generic functions: regular-seeming functions that can be invoked just like any other function, but that automatically dispatch methods based on the combination of arguments passed to it when it's called, also known as multiple dispatch.

It was inspired by Slate, CLOS and Sheeple.

Install

$ npm install genfun

Table of Contents

Example

Various examples are available to look at in the examples/ folder included in this project. Most examples are also runnable by just invoking them with node.

import Genfun from "genfun"

class Person {}
class Dog {}

const frobnicate = Genfun()

frobnicate.add([Person], (person) => {
  console.log('Got a person!')
})

frobnicate.add([Dog], (dog) => {
  console.log('Got a dog!')
})

frobnicate.add([String, Person, Dog], (greeting, person, dog) => {
  console.log(person, ' greets ', dog, ', \'' + greeting + '\'')
})

const person = new Person()
const dog = new Dog()

frobnicate(person) // Got a person!
frobnicate(dog) // Got a dog!
frobnicate('Hi, dog!', person, dog); // {} greets {}, 'Hi, dog!'

API

The basic API for Genfun is fairly simple: You create a new genfun by calling Genfun(), and add methods to them. Then you call the genfun object like a regular function, and it takes care of dispatching the appropriate methods!

Genfun()

Takes no arguments. Simply creates a new genfun. A genfun is a regular function object with overriden function call/dispatch behavior.

When called, it will look at its arguments and determine if a matching method has been defined that applies to all arguments passed in, considered together.

New methods may be added to the genfun object with gf.add().

If no method is found, or none has been defined, it will invoke Genfun.noApplicableMethod with the appropriate arguments.

Genfuns preserve the value of this if invoked using .call or .apply.

Example
var gf = Genfun()

//... add some methods ..

// These calls are all identical.
gf(1, 2, 3)
gf.call(null, 1, 2, 3)
gf.apply(null, [1, 2, 3])

gf.add(<selector>, <body>)

Adds a new method to gf and returns gf to allow chaining multiple adds.

<selector> must be an array of objects that will receive new Roles (dispatch positions) for the method. If an object in the selector is a function, its .prototype field will receive the new Role. The array must not contain any frozen objects.

When a genfun is called (like a function), it will look at its set of added methods and, based on the Roles assigned, and corresponding prototype chains, will determine which method, if any, will be invoked. On invocation, a method's <body> argument will be the called with the arguments passed to the genfun, including its this and arguments values`.

Within the <body>, Genfun.callNextMethod may be called.

Example

var numStr = Genfun()

numStr.add([String, Number], function (str, num) {
  console.log('got a str:', str, 'and a num: ', num)
})

numStr.add([Number, String], function (num, str) {
  console.log('got a num:', num, 'and a str:', str)
})

Genfun.callNextMethod([...<arguments>])

NOTE: This function can only be called synchronously. To call it asynchronously (for example, in a Promise or in a callback), use getContext

Calls the "next" applicable method in the method chain. Can only be called within the body of a method.

If no arguments are given, callNextMethod will pass the current method's original arguments to the next method.

If arguments are passed to callNextMethod, it will invoke the next applicable method (based on the original method list calculation), with the given arguments, even if they would otherwise not have triggered that method.

Returns whatever value the next method returns.

There must be a next method available when invoked. This function will not call noApplicableMethod when it runs out of methods to call. It will instead throw an error.

Example
class Foo {}
class Bar extends Foo {}

var cnm = Genfun()

cnm.add([Foo], function (foo) {
  console.log('calling the method on Foo with', foo)
  return foo
})

cnm.add([Bar], function (bar) {
  console.log('calling the method on Bar with', bar)
  return Genfun.callNextMethod('some other value!')
})

cnm(new Bar())
// calling the method on Bar with {}
// calling the method on Foo with "some other value!"
// => 'some other value!'

Genfun.getContext()

The context returned by this function will have a callNextMethod method which can be used to invoke the correct next method even during asynchronous calls (for example, when used in a callback or a Promise).

This function must be called synchronously within the body of the method before any asynchronous calls, and will error if invoked outside the context of a method call.

Example
someGenfun.add([MyThing], function (thing) {
  const ctx = Genfun.getContext()
  return somePromisedCall(thing).then(res => ctx.callNextMethod(res))
})

Genfun.noApplicableMethod(<gf>, <this>, <args>)

Genfun.noApplicableMethod is a genfun itself, which is called whenever any genfun fails to find a matching method for its given arguments.

It will be called with the genfun as its first argument, then the this value, and then the arguments it was called with.

By default, this will simply throw a NoApplicableMethod error.

Users may override this behavior for particular genfun and this combinations, although args will always be an Array. The value returned from the dispatched noApplicableMethod method will be returned by genfun as if it had been its original method. Comparable to Ruby's method_missing.

Performance

Genfun pulls a few caching tricks to make sure dispatch, specially for common cases, is as fast as possible.

How fast? Well, not much slower than native methods:

Regular function: 30.402ms
Native method: 28.109ms
Singly-dispatched genfun: 64.467ms
Double-dispatched genfun: 70.052ms
Double-dispatched genfun with string primitive: 76.742ms