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

seshet

v0.1.0

Published

Functional memoization Utility

Downloads

3,304

Readme

Seshet

Javascript Functional Memorization Utility

About

Seshat defines a set of operations for working with a two dimensional data structure for use with memoization. One use case is memoizing the results of computations in sequence, especially for parsing. The exposed interfaces are all functional-style.

In most cases, Seshat has good access and update performance by using a self balancing tree. The tree can also be pruned to discard unreachable values.

Example

Calculating the Fibonacci sequence is the classic example of why memorization matters. Although not the best application of Seshat, this demonstration implements the Fibonacci sequence using Seshat.

Basic Algorithm

The basic logic for calculating Fibonacci number n is:

var fib = \n ->
    (n < 2 ? n
        fib(n - 1) + fib(n -2));

Written using continuations for clearer translation:

var fib = \n, k ->
    (n < 2 ?
        k(n)
        fib(n - 1, \x ->
            fib(n - 2, \y ->
                k(x + y))));

The problem with this approach is that fib(n - 1) and fib(n - 2) are recalculated on every call, even when the calculation has been performed before. Memoization stores past results in a table to provide constant time lookup. In this problem, only the previous two values need to be stored.

Basic Memoization

Seshat operates on an memoization data structure, so fib is rewritten to take the opaque memoization table as an argument. The memo table is immutable, and needs to be threaded though the continuations.

Vales are stored using two dimensional keys. The first part of the key is used for storing the value in a tree and the second part for looking up a specific instance of the value on a node. In the Fibonacci function, only one dimensional storage is needed. The first part of the key will be the Fibonacci number, and the second will always be zero.

Updating the fib function to take advantage of Seshat, before entering the Fibonacci logic, the memo table is checked. If a result is found, it is returned right away. Otherwise, the calculation is performed and the result is stored:

var fib = \n, m, k -> {
    var found = seshat.lookup(m, x, 0);
    if (found !== null)
        return k(found, m);
    return (n < 2 ?
        k(n, seshat.update(m, n, 0, n)) :
        fib(n - 1, m, \x, m ->
            fib(n - 2, m, \y, m ->
                k(x + y, seshat.update(m, n, 0, x + y)))));
};

Now large Fibonacci numbers can be calculated. A key ordering function compareInt is defined for the keys of the memoization table. On the first call of fib, a new, empty memoer is created.

var compareInt = \x, y ->  x - y;

// JS nums round this from the real result
fib(100, seshat.create(compareInt), \x, m -> x); // 354224848179262000000

Pruning

By passing in a continuation that returns the memoization table, this table can be inspected (This is for demonstration purposes only, the table should always be treated as an opaque data structure):

var m = fib(100, seshat.create(compareInt), \_, m -> m);

var count = \root ->
    (!root ? 0 :
        1 + count(root.left) + count(root.right));

count(m.root); // 101 with values from [0 .. 100]

Although 101 entries are stored, only the last two results in the calculations are ever used. Numbers before n - 2 are unreachable. The unreachable entries can be pruned to reduce the size of the tree:

var fib = \n, m, k -> {
    var found = seshat.lookup(m, x, 0);
    if (found !== null)
        return k(found, m);
    return (n < 2 ?
        k(n, seshat.update(m, n, 0, n)) :
        fib(n - 1, m, \x, m ->
            fib(n - 2, m, \y, m ->
                k(x + y, seshat.prune(seshat.update(m, n, 0, x + y), n - 1)))));
};

var m = fib(100, seshat.create(compareInt), \_, m -> m);
count(m.root); // 2 with values for [99, 100]

The prune uses n - 1 as the lower bound because the lower bound is inclusive and only values for n and n - 1 are needed. Although not particularly beneficial in this case, pruning can reduce memory usage and improving access performance.

Hof

Seshat is not designed for direct use. This simplified example demonstrates using Seshat for memoization of a monadic Fibonacci calculator:

// Basic Operations
var ret = \x -> \m, k -> k(x, m);
var bind = \c, f -> \m, k -> c(m, \x, m -> f(x)(m, k));
var next = \p, c -> bind(p, \() -> c);

var modifyM = \f -> \m, k -> let x = f(m) in k(x, x);
var getM = modifyM(\m -> m);

// Memoer Operations
var update = \key, val ->
    next(
        modifyM(\m -> seshat.update(m, key, 0, val)),
        ret(val));

var lookup = \key, fallback ->
    bind(getM, \m -> 
        let found = seshat.lookup(m, key, 0) in
            (found !== null ? ret(found) : fallback));

// Fibonacci
var fib = \n ->
    (n < 2 ? ret(n) :
        lookup(n, bind(fib(n - 1), \x ->
            bind(fib(n - 2), \y ->
                update(n, x + y)))));

fib(100)(seshat.create(compareInt), \x -> x)); //354224848179262000000