@tamerfrombk/optional
v2.0.0
Published
A minimal implementation of an Optional data type mirroring Java 11's optional.
Downloads
5
Readme
Optional
A minimal implementation of an Optional data type mirroring Java 11's optional.
Quickstart
npm i @tamerfrombk/optional
Features
- Lightweight
typescript
is the only dependency!- single source file:
src/optional.{ts,js}
- Functional
- the Optional type does not mutate itself and follows functional programming best practices
- 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
- Create a feature/issue on GitHub.
- Create a PR addressing the feature/issue.
- Have it reviewed and merged :)