2.0.0 • Published 1 year ago

@tamerfrombk/optional v2.0.0

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
1 year ago

Optional

A minimal implementation of an Optional data type mirroring Java 11's optional.

Quickstart

npm i @tamerfrombk/optional

Features

  1. Lightweight
    • typescript is the only dependency!
    • single source file: src/optional.{ts,js}
  2. Functional
    • the Optional type does not mutate itself and follows functional programming best practices
  3. No surprises!
    • The library works as you'd expect it to per PLA.

Examples

Constructing an Optional

// opt is an Optional<string> holding the value 'abc'
const opt: Optional<string> = Optional.of('abc');

// False-y values must use ofFalsy() or an exception will be thrown
const opt2: Optional<string> = Optional.ofFalsy('');

Manipulating an Optional

const mapper = (s) => s + ' def';

// value will be 'abc def'
const value: string = Optional.of('abc').map(mapper).orElse('other');

// otherValue will be 'other'
const otherValue: string = Optional.ofFalsy('').map(mapper).orElse('other');

Dealing with potentially "falsy" values

const value: string = myServiceThatCanReturnFalsy.doSomething(...);

// safeValue will be 'foo' in case of a falsy value
// or a value that is not equal to 'abc' with the mapper applied to it
const safeValue = Optional.ofFalsy(value)
    .filter(s => s !== 'abc')
    .map(s => /* do something with s */)
    .orElse('foo');

Philosophy

Tony Hoare, a computer scientist that invented ALGOL in 1965, famously gave a talk in 2009 where he apologizes for having null in the language and called it his "billion dollar mistake". You've probably felt the effects of his "mistake" when your program either mysteriously crashes or throws a NullPointerException; this is usually due to forgetting to check whether a certain object was null before using it. This is a common defensive programming pitfall.

The truth of the matter is that defensive programming is only going to be so effective: programmers are still human and humans forget things all the time. So what do we do if defensive programming won't help us much? We turn to computers -- more specifically compilers -- for help.

The Optional data type is inspired by the functional programming equivalent of a Maybe data type; it represents the idea of a value possibly existing right into the type system. This means that the compiler can help us catch null errors during compilation before the program is ever shipped.

Let's walk through a quick example.

Suppose you have the following service:

class AccountService {
  findById(id: number): Account {
    // implementation
  }
}

The returned Account object may or may not be there and we have to deal with both of those possibilities. We can deal with it either by checking for the "truthyness" of the Account each time we call findById or we can have findById do the check and throw if the account could not be found. If we decide to throw, we have now opened another can of worms for us to deal with.

This is exactly where Optional shines; instead of throwing or repeatedly checking the Account, we ask the type system for help:

import { Optional } from '@tamerfrombk/optional';

class AccountService {
  findById(id: number): Optional<Account> {
    return Optional.ofFalsy(/* implementation */);
  }
}

In our client code, if we try to treat the Optional as an Account, we'll get a compile time error:

import AccountService from './services/AccountService';

function myFunction() {
  const account = AccountService.findById(0);

  const balance = account.deposit(100); // compiler error -- no deposit() method on Optional
}

The compiler is forcing us to deal with the possibility the Account may not exist thanks to the Optional type. Here's one way of handling this possibility:

import AccountService from './services/AccountService';

function myFunction() {
  const balance = AccountService.findById(0)
    .map((a) => a.deposit(100))
    .orElse(-1);
}

Now we've made handling the possibility of a missing account explicit.

Contributing

  1. Create a feature/issue on GitHub.
  2. Create a PR addressing the feature/issue.
  3. Have it reviewed and merged :)
2.0.0

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago