JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 94
  • Score
    100M100P100Q71006F
  • License MIT

Wrap regular functions & promises to make it possible to handle all 3 outcomes differently -- result, native exception, & application error.

Package Exports

  • tri-fp

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (tri-fp) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

tri-fp 🔱

Wrap regular functions & promises to make it possible to handle all 3 outcomes differently -- result, native exception, & application error.

                     +--------+
+----------+    +--->| Result |
| Function |----|    +--------+
+----------+    |
                |    +------------------+
 +---------+    |--->| Native exception |
 | Promise |----|    +------------------+
 +---------+    |
                |    +-------------------+
                +--->| Application error |
                     +-------------------+

This is not the case with regular JavaScript. Application errors and native exceptions are mixed. However, you probably want to handle the first category gracefully & let the user know, and on the contrast make your actual bugs let you know about them quickly by crashing. Read about the motivation of all this in some references in a section below.

This module aims to:

  • Make both functions and promises more functional by removing the need for try-catch.
  • Handle application errors and native exceptions differently.
  • Not alter native JS constructs, instead only provide tiny wrappers.
  • Increase readability & control.

Get Started

npm i tri-fp

tri & triP

Wrap a function in tri, or a promise in triP, to enable the handling of all 3 outcome categories differently:

  • Native exceptions are still thrown.
  • The return from the new function/promise will be an error-first pair, [error, result].
import { tri, triP } from "tri-fp";

// Sync
const [error, result] = tri(myFunction)(arg1, arg2);

// Async, direct promise
const [error, result] = await triP(myPromise);

// Async, promise returning function
const [error, result] = await triP(myPromiseReturningFunc)(arg1, arg2);

// Native errors/exceptions will still throw.
// Don't catch these; Find your bug.

// Of course check the error in some way after using one of the above:
if (error) // ...

bi & biP

Sometimes we actually know that native exceptions can be treated as application errors. Thus, a bi/biP wrapper is also provided ("bi" for splitting to only 2 outcomes instead of 3/"tri") that convert native exceptions to application errors (Error instance). (See "Advanced use" section below to make your own Error conversion).

import { bi, biP } from "tri-fp";

// WARNING! Will not throw native exceptions, but convert them to Error.
// Don't use this if you're not sure why.
const [error, result] = bi(myFunction)(arg1, arg2);
const [error, result] = await biP(myPromise);
const [error, result] = await biP(myPromiseReturningFunc)(arg1, arg2);

One example where bi would be useful could be handling of dynamic JSON:

import { bi } from "tri-fp";

const saferParse = bi(JSON.parse);
const [error, result] = saferParse(data);

if (error) {
  showSnackbar(`Corrupt data package: ${error.message}`);
  console.warn(error);
}

tryCatch & tryCatchP

For completeness (& only 2 extra lines of code in this source) a basic try-catch wrapper is also provided: tryCatch/tryCatchP that only converts try-catch patterns to error-first pairs. These 2 functions are however not recommended -- you should use one of the above, first and foremost tri/triP.

import { tryCatch, tryCatchP } from "tri-fp";

// DOUBLE WARNING! This will keep native exceptions as is, but not throw them!
const [error, result] = tryCatch(myFunction)(arg1, arg2);
const [error, result] = await tryCatchP(myPromise);
const [error, result] = await tryCatchP(myPromiseReturningFunc)(arg1, arg2);

Motivation

Read about the subject of problems with Promises and why to avoid using throw, try/catch, etc. in some references below.

The motivation for 2 different names, tri & triP is to keep it, or make it, clear that a promise is something different where we need to use e.g. await. We can then also support promise returning functions, which is a preferred way to create promises anyway (closer to a more functional "task").

Advanced use

Custom Error Transform

It is possible to access the raw wrappers, and provide your own Error transforming function.

import { tryWrap, tryWrapP } from "tri-fp";

const throwAllBut = (errorTypes) => (err) => {
  if (errorTypes.includes(err.constructor)) return err;
  throw err;
};

const tryFunc = tryWrap(throwAllBut([MyError]));

const [error, result] = tryFunc(myFunction);
// Perhaps error == MyError

Use tryWrapP the same way, and it will work like triP/biP/tryCatchP.

Empty Value Configuration

Another additional feature is that it is possible to configure that all empty values instead of undefined should be some other value, i.e. null.

import { setNoneValue } from "tri-fp";

setNoneValue(null);

// All uses of the tri-fp lib will now return null at empty spaces in the error-first pairs.

Use with Either

If you want to use the even more fp style Either type, here is an example with sanctuary.

import * as S from 'sanctuary';
import { tri as baseTri } from "tri-fp";

// arrayPairToEither :: (Array2 Error a) -> (Either Error a)
const arrayPairToEither = ([e, r]) => e ? S.Left(e) : S.Right(r);

// tri :: (*... -> a) -> (*... -> (Either Error a))
const tri = f => S.unchecked.pipe([baseTri(f), arrayPairToEither]);
// Now use tri as described above

Recommended eslint

eslint-plugin-fp

{
  "plugins": ["fp"],
  "rules": {
    "fp/no-throw": ["warn"]
  }
}

Avoid using try-catch or .catch.

Inspired by / Thanks to

Licence

MIT