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

kanskje

v2.0.2

Published

Simple Maybe monad written in TypeScript

Downloads

9

Readme

kanskje

Simple Maybe monad written in TypeScript

npm module Coverage status Build status dependencies status devDependencies status Code style: Prettier

Set your code free

Table of Contents

Introduction

A Maybe represents a wrapper around any value - most commonly values that might be nullable. Having this wrapper is useful because it guards against null and undefined when doing computations on the value. The idea is that the Maybe deals with nullables internally, thus allowing one to write simpler code with fewer conditionals. Not until the very end when it's needed to unwrap the value again, is it necessary to deal with the fact that it was or somehow became nullable.

Maybe.fromNullable(getValueThatMightNotBeThere())
  .map(doSomething)
  .map(doAnotherThing)
  .map(doFinalThing)
  .getOrElse(defaultValue);

Even though they can't be constructed individually, the Maybe consists of two classes: Just and Nothing. The Maybe is a Just if it holds a value and a Nothing if it doesn't.

const toUpper = a => a.toUpperCase();

Maybe.fromNullable(['foo', 'bar', 'baz'][2]) // Just('baz')
  .map(toUpper) // Just('BAZ')
  .getOrElse('No value here');
// => 'BAZ'

Maybe.fromNullable(['foo', 'bar', 'baz'][3]) // Nothing
  .map(toUpper) // Nothing
  .getOrElse('No value here');
// => 'No value here'

There exists a number of great resources on Maybe monads - including The Marvellously Mysterious JavaScript Maybe Monad by @jrsinclair and Professor Frisby's Mostly Adequate Guide to Function Programming by @drboolean - that one might want to get familiar with. If you are used to Promises, you are essentially already familiar with monads.

Why kanskje?

kanskje stems from the need of a simple Maybe monad with type declarations making it usable for both JavaScript and TypeScript. The fact that the Maybe is written in TypeScript makes sure that it only contains methods that are actually possible to express using the TypeScript type system.

The source code is simple with one-line functions and no aliases, and yet the function declarations - using Generic Types of TypeScript - should be self-documenting.

Unlike some Maybe monads, kanskje doesn't perform behind the scenes conversions from Just to Nothing. As an example you can pass any mapper function, f: (a: A) => B, to map and be sure that the return type of f isn't checked. A Just is kept a Just even if f returns a nullable:

const unsafeProp = b => a => a[b];

Maybe.of({ name: 'Alice' }) // Just({ name: 'Alice' })
  .map(unsafeProp('age')) // Just(undefined)
  .getOrElse(25);
// => undefined

If Just(undefined) is not the desired outcome, the mapper function, f, needs to return a Maybe and be passed to chain instead:

const safeProp = b => a => Maybe.fromNullable(a[b]);

Maybe.of({ name: 'Alice' }) // Just({ name: 'Alice' })
  .chain(safeProp('age')) // Nothing
  .getOrElse(25);
// => 25

Usage

This is a CommonJS module.

Import the exported functions as named imports:

const { all, empty, fromNullable, of } = require('kanskje');

Or namespace them all under e.g. Maybe:

const Maybe = require('kanskje');

Or use ES2015 module syntax if your environment supports that:

import { all, empty, fromNullable, of } from 'kanskje';

import * as Maybe from 'kanskje';

API

Constructing Maybes

To construct a Maybe one of the following functions can be used:

all

Accepts an array or a tuple of Maybes and returns a single Maybe. If all the Maybes were Justs, the resulting Maybe will be a Just containing their values. If any of the Maybes where a Nothing, the resulting Maybe will be a Nothing.

  • Signature:

    all<A>(maybes: Maybe<A>[]): Maybe<A[]>
    
    all<A, B>(maybes: [Maybe<A>, Maybe<B>]): Maybe<[A, B]>
    
    all<A, B, C>(maybes: [Maybe<A>, Maybe<B>, Maybe<C>]): Maybe<[A, B, C]>
    
    all<A, B, C, D>(
      maybes: [Maybe<A>, Maybe<B>, Maybe<C>, Maybe<D>]
    ): Maybe<[A, B, C, D]>
    
    all<A, B, C, D, E>(
      maybes: [Maybe<A>, Maybe<B>, Maybe<C>, Maybe<D>, Maybe<E>]
    ): Maybe<[A, B, C, D, E]>
  • Example:

    Maybe.all([Maybe.of('foo'), Maybe.of('bar'), Maybe.of('baz')]);
    // => Just(['foo', 'bar', 'baz'])
    Maybe.all([Maybe.of('foo'[1]), Maybe.of('bar'[3]), Maybe.of('baz'[2])]);
    // => Just(['o', undefined, 'z'])
    Maybe.all([
      Maybe.of('foo'[1]),
      Maybe.fromNullable('bar'[3]),
      Maybe.of('baz'[2]),
    ]);
    // => Nothing

empty

Returns a Nothing.

  • Signature:

    empty<A>(): Maybe<A>
  • Example:

    Maybe.empty();
    // => Nothing

fromNullable

Lifts a value into a Maybe but checks if the value is either null or undefined. If that is a case, a Nothing is returned. Otherwise a Just is returned.

  • Signature:

    fromNullable<A>(a: Nullable<A>): Maybe<A>
  • Example:

    Maybe.fromNullable('foo');
    // => Just('foo')
    Maybe.fromNullable(['foo', 'bar', 'baz'][3]);
    // => Nothing

of

Lifts a value into a Maybe, more specifically: a Just.

  • Signature:

    of<A>(a: A): Maybe<A>
  • Example:

    Maybe.of('foo');
    // => Just('foo')
    Maybe.of(['foo', 'bar', 'baz'][3]);
    // => Just(undefined)

Maybe methods

Once a Maybe is constructed, the following methods are accessible on the instance:

chain

Accepts a mapper function, f, that returns a Maybe and automatically unwraps the outer Maybe to not end up with a Maybe<Maybe<B>>.

  • Signature:

    chain<B>(f: (a: A) => Maybe<B>): Maybe<B>
  • Example:

    const safeHead = xs => Maybe.fromNullable(xs[0]);
    
    Maybe.of([1, 2, 3]).chain(safeHead);
    // => Just(1)
    
    Maybe.of([]).chain(safeHead);
    // => Nothing

filter

Accepts a predicate function, f, and converts the Maybe from a Just to a Nothing if its value does not adhere. If the Maybe is already a Nothing it remains a Nothing.

Note: If used with TypeScript f can be a Type Guard.

  • Signature:

    filter(f: (a: A) => boolean): Maybe<A>
    filter<B extends A>(f: (a: A) => a is B): Maybe<B>
  • Examples:

    Using a predicate function:

    const isEven = a => a % 2 === 0;
    
    Maybe.of(4).filter(isEven);
    // => Just(4)
    
    Maybe.of(7).filter(isEven);
    // => Nothing

    Using a TypeScript Type Guard:

    interface Admin extends Person {
      password: string;
    }
    
    interface Person {
      name: string;
    }
    
    function isAdmin(a: Person): a is Admin {
      return a.hasOwnProperty('password');
    }
    
    const carl: Admin = {
      name: 'Carl',
      password: '1234',
    };
    
    const persons: Person[] = [
      {
        name: 'Alice',
      },
      carl,
    ];
    
    Maybe.fromNullable(persons[0]).filter(isAdmin);
    // => Nothing
    
    Maybe.fromNullable(persons[1]).filter(isAdmin);
    // => Just({ name: 'Carl', password: '1234' })

fold

Accepts two functions: a mapper function, f, and a function with the same return type, g. If the Maybe is a Just its value is mapped and returned using f. If it's a Nothing the result of g is returned.

  • Signature:

    fold<B>(f: (a: A) => B, g: () => B): B
  • Example:

    const unsafeProp = b => a => a[b];
    
    const persons = [
      {
        name: 'Alice',
      },
      {
        name: 'Bob',
      },
    ];
    
    Maybe.fromNullable(persons[1]).fold(
      unsafeProp('name'),
      () => 'A person does not exist',
    );
    // => 'Bob'
    
    Maybe.fromNullable(persons[2]).fold(
      unsafeProp('name'),
      () => 'A person does not exist',
    );
    // => 'A person does not exist'

getOrElse

Accepts a default value, a, and returns that if the Maybe is a Nothing. Otherwise the value of the Just is returned.

  • Signature:

    getOrElse(a: A): A
  • Example:

    Maybe.fromNullable(getPortFromProcess()).getOrElse(3000);

isJust

Returns true if the Maybe is a Just and false if it's a Nothing.

  • Signature:

    isJust(): boolean

isNothing

Returns true if the Maybe is a Nothing and false if it's a Just.

  • Signature:

    isNothing(): boolean

map

Accepts any mapper function, f, and applies it to the value of the Maybe. If f returns a Maybe, the result will be a nested Maybe.

  • Signature:

    map<B>(f: (a: A) => B): Maybe<B>
  • Example:

    const length = a => a.length;
    
    Maybe.of('foo').map(length);
    // => Just(3)
    const safeHead = xs => Maybe.fromNullable(xs[0]);
    
    Maybe.of([1, 2, 3]).map(safeHead);
    // => Just(Just(1))
    
    Maybe.of([]).map(safeHead);
    // => Just(Nothing)

orElse

Accepts a Maybe, a. If the instance Maybe is a Nothing it is replaced with a. Otherwise it is left unchanged.

  • Signature:

    orElse(a: Maybe<A>): Maybe<A>
  • Example:

    const persons = [
      {
        name: 'Alice',
      },
      {
        name: 'Bob',
      },
    ];
    
    Maybe.fromNullable(persons[1]).orElse(Maybe.of({ name: 'Carl' }));
    // => Just('Bob')
    
    Maybe.fromNullable(persons[2]).orElse(Maybe.of({ name: 'Carl' }));
    // => Just('Carl')

unsafeGet

Unsafely unwraps the value from the Maybe. Since Nothings don't contain a value, the function will throw an error if the Maybe happened to be a Nothing.

  • Signature:

    unsafeGet(): A | void
  • Example:

    Maybe.fromNullable(5).unsafeGet();
    // => 5
    
    Maybe.of(undefined).unsafeGet();
    // => undefined
    
    Maybe.fromNullable(undefined).unsafeGet();
    // => TypeError: A Nothing holds no value.