Nonstop Progressive Marxism

    tri-fp

    3.0.3 • Public • Published

    tri-fp 🔱

    Eliminate try-catch when you code, AND get access to all 3 outcomes of "throwers".

    • 0 dependencies
    • 100% test coverage
    • 400 bytes minified & compressed

    Sneak peak

    import { tri } from "tri-fp";
    
    const [error, result] = tri(myFuncThatMightThrow)(arg1, arg2);
    if (error) // ...
    // continue with result

    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.

    Wrap regular functions & promises to make it possible to handle all 3 outcomes differently: Results, Native Exceptions, & Application Errors.

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

    What? Why?

    In JavaScript, application errors and native exceptions are mixed and can not be easily separated and handled differently. You should handle the first category gracefully and let the user know, and on the contrast make your actual bugs let you know about them quickly by crashing. Read more about this under "Motivation" below.

    Get Started

    npm i tri-fp

    API

    tri & triAsync

    Wrap a function in tri, or a promise in triAsync, 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, triAsync } from "tri-fp";
    
    // Sync
    const [error, result] = tri(myFunction)(arg1, arg2);
    
    // Async, direct promise
    const [error, result] = await triAsync(myPromise);
    
    // Async, promise returning function
    const [error, result] = await triAsync(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 & biAsync

    Sometimes we actually know that native exceptions can be treated as application errors (e.g. JSON.parse). Thus, a bi/biAsync 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, biAsync } 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 biAsync(myPromise);
    const [error, result] = await biAsync(myPromiseReturningFunc)(arg1, arg2);

    One example where bi would be useful is 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 & tryCatchAsync

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

    import { tryCatch, tryCatchAsync } 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 tryCatchAsync(myPromise);
    const [error, result] = await tryCatchAsync(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 & triAsync 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, tryWrapAsync } 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 tryWrapAsync the same way, and it will work like triAsync/biAsync/tryCatchAsync.

    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 eslint-plugin-functional

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

    Avoid using try-catch or .catch.

    Inspired by / Thanks to

    Licence

    Hippocratic 2.1

    Install

    npm i tri-fp

    DownloadsWeekly Downloads

    54

    Version

    3.0.3

    License

    Hippocratic-2.1

    Unpacked Size

    50.8 kB

    Total Files

    19

    Last publish

    Collaborators

    • ez