5 comments

[ 0.28 ms ] story [ 25.0 ms ] thread
Hi HN.

I wrote an Option object for javascript and typescript.

Lots of functional programming libraries already exist, in addition to several standalone JS/TS option packages. So why another?

I wanted a high quality, stand-alone, fully typed package with great behavior. I believe this is the best package on npm for working with Options.

Benefits

- Excellent TypeScript support. The developer experience is exceptional.

- Zero production dependencies. Even the dev dependency list is extremely small.

- Safe and powerful implementation of some methods. Flatten in most packages will throw an error called on Nones.This package never throws. Better implementation of contains method - this package lets you pass in a custom equality function (for example deep object equality from another package or maybe based on a specific property). By default the function is ===. Suitable for primitive values.

- Static (aka curried) versions of map (also called lift) and flatMap. This allows for converting functions that work on non option values to their equivalent that work on Optionals and allows you to reuse your existing code as much as possible.

- Great documentation. Lots of examples in TSDocs that will show in your editor for support. Tests demonstrating usage, and extensive information in the README.

- Simple (but powerful) implementation. Makes for a great example to follow along for anyone learning functional programming while providing an easy way to get working code for all projects.

I hope you take a look and find this interesting or useful!

> Without options, null and undefined checks are often necessary before manipulating a function's argument (even in TS codebases).

Not if you use `strictNullChecks` in Typescript, right...

strictNullChecks is great but they don't cover everything.

Take this trivial example:

    const maybeReturn = () => {
        if (Math.random() > .5) {
            return 10
        }
    }

    const main = () => {
        const val = x();

        // With strictNullChecks, this checking is still necessary
        return !!val ? val * 10 : val;
    }
Callers of maybeReturn will have to narrow the return value's type to confirm it actually is a number before working with the variable. In fact, callers of main will also have to check the type since the undefined is carried through when its falsy!

There are other TS/ESLint rules to help avoid this (consistent return for example). But these situations are common. Even with eslint rules to mitigate this, they can be satisfied by specifically returning null or undefined which defeats the purpose.

With options, it's much simpler.

    const maybeReturnWithOption = () => {
        if (Math.random() > .5) {
            return Some(10)
        } 

        return None();
    }

    const mainWithOptions = () => {
        const val = maybeReturnWithOption();

        // No conditional checks necessary. And callers don't need to check. 
        return val.map(x => x * 10);
    }
Returning None more clearly demonstrates intent as well when you really don't want to return anything (but want to avoid returning null/undefined) and avoids having to come up with conventions for returning specific values that signify null/undefined by callers.
To each their own. To me

  return !!val ? val \* 10 : val;
and

  return val.map(x => x \* 10);
are equivalent. They're both confirming the presence of a value before using it. And callers of both main/mainWithOptions will have to do likewise.

> Returning None more clearly demonstrates intent as well when you really don't want to return anything (but want to avoid returning null/undefined) and avoids having to come up with conventions for returning specific values that signify null/undefined by callers.

Null and undefined are values that are used for this exact purpose, though...

The difference here is callers of mainWithOptions won't have to do the null/undefined check again.

They get back an option so they can just keep calling map.

And that's true all the way up the call stack.

But in the main example, since it may always return null/undefined, every caller up the stack will also have to do this check.

Null and undefined are used for lots of different purposes by lots of different people. None (and Options more generally) is much more specific - there may or may not be a value here but you can safely work with this value regardless without needing to first check.

The checking is built into the object for you.

Neither way is right, just about what you prefer. I find the ergonomics of options much nicer than null/undefined.