Narnia's Psychedelic Mushrooms

    tryad

    1.0.0 • Public • Published

    tryad · Version License Travis CI JavaScript Standard Style

    tryad

    Monadic mashup of Maybe & Either that represents a value, nothing, or an error.

    There are a few major reasons for tryad to exist:

    • most existing Maybe / Option / Either / Result types in JavaScript don't seamlessly interop with Promises
    • if the Maybe contains an array, you have to map inside a map because it doesn't dispatch
    • toMaybe and toEither become unnecessary if the type simply has a third branch to cover exceptions

    So tryad attempts to create a new type that incorporates all of this - replacing both null-ish checks and try/catch while working well with Promises.

    installation

    npm i tryad

    usage

    import tryad from 'tryad'
    import { resolve } from 'path'
     
    const loadPlugin = atPath => {
      return require(atPath).initialize()
    }
     
    const plugin = { name: 'a-plugin' /*, path: './plugin.js' */ }
    const loaded = tryad(plugin.path)
      .map(path => resolve(process.cwd(), path))
      .try(absolute => loadPlugin(absolute))
      .unwrap(
        // `loaded` becomes equal to the return value of
        // whichever branch we hit here in `unwrap`
        Some => 'loaded the plugin'
        None => 'plugin.path was not defined, so nothing was executed'
        Fail => 'a wild error appeared! ' + Fail.message
      )
     
    // in this case `path` is not defined, so we get a `None`:
    // -> 'plugin.path was not defined, so nothing was executed'

    Or if you're all about that async, let's check out Promises!

    import tryad from 'tryad'
    import { readJson } from 'fs-extra'
     
    ;(async () => {
      const result = await tryad('./package.json')
        // returning a Promise here like `readJson` does
        // makes the chain's result become asynchronous
        .try(path => readJson(path))
        .map(pkg => pkg.keywords)
        // methods dispatch to the inner value, so this
        // maps over each keyword in the array
        .map(key => key.toLowerCase())
        .filter(key => key === 'maybe')
        .unwrap(
          Some => 'found the maybe keyword',
          None => 'did not find the maybe keyword',
          Fail => 'an error occurred! ' + Fail.message
        )
     
      /*...*/
    })()

    And remember that if at any point a transformation causes a nil / falsy value to be returned or if .try() throws an Error, the following transformations will not be executed. This makes null handling very easy - in this case the chain would 'skip forward' to the unwrap call.

    api

    tryad

    tryad(value)

    Returns a new object that is either a None if value == null or a Some containing value otherwise.

    Expand the following sections to see the full documentation.

    static methods

    of

    tryad.of(value)

    Alternate constructor that checks for truthiness rather than loose equality to null.

    try

    tryad.try(value, fn)

    Shortcut for tryad.of(value).try(fn). If value != null it will be applied to the function fn, and any errors will be caught to transform the tryad into a Fail. fn is only executed on a Some.

    Arguments

    • value: any

    • fn: value -> any

      Function that will receive value as its only parameter. All errors are caught and returned in a new Fail object.

    Returns Some | None | Fail

    async

    tryad.async(value)

    Asynchronous version of the tryad constructor that accepts value as a Promise, or wraps it in one if it isn't already a Promise.

    async.of

    tryad.async.of(value)

    Asynchronous version of the tryad.of constructor that accepts value as a Promise, or wraps it in one if it isn't already a Promise.

    isSomeLike

    tryad.isSomeLike(value)

    Useful for checking if an arbitrary object is a Some, meaning it is not == null and has an isSome method that returns true.

    Arguments

    • value: any

    Returns Boolean

    isNoneLike

    tryad.isNoneLike(value)

    Useful for checking if an arbitrary object is a None, meaning it is not == null and has an isNone method that returns true.

    Arguments

    • value: any

    Returns Boolean

    isFailLike

    tryad.isFailLike(value)

    Useful for checking if an arbitrary object is a Fail, meaning it is not == null and has an isFail method that returns true.

    Arguments

    • value: any

    Returns Boolean

    instance methods

    These methods are callable on instances of a Some, None, or Fail. Examples will use the name box to represent one of these instances.

    filter

    box.filter(fn)

    fn will only be executed if box is a Some, and receives its value as its only argument. If it returns falsy, a None will be returned. If it returns truthy, the current instance is returned as-is. Dispatches to value.filter() if it is callable.

    Arguments

    • fn: (value) -> Boolean

    Returns Some | None

    flatMap

    box.flatMap(fn)

    fn will only be executed if box is a Some, and receives its value as its only argument. Any returned tryad will be absorbed. Dispatches to value.flatMap() if it is callable.

    Arguments

    • fn: (value) -> Some | None | Fail

    Returns Some | None | Fail

    forEach

    box.forEach(fn)

    fn will only be executed if box is a Some, and receives its value as its only argument. This method ends the chain. Dispatches to value.forEach() if it is callable.

    Arguments

    • fn: (value) -> any

    Returns undefined

    includes

    box.includes(other)

    If box is a Some, compares other against the value contained within box and returns true if they are equal. Returns false if box is not a Some. Dispatches to value.includes() if it is callable.

    Returns Boolean

    map

    box.map(fn)

    fn will only be executed if box is a Some, and receives its value as its only argument. The return value will be used to construct a new tryad. If fn will return a tryad, you probably want to use flatMap instead. Dispatches to value.map() if it is callable.

    Arguments

    • fn: (value) -> any

    Returns Some | None

    orElse

    box.orElse(fn)

    fn is only called if box is a None or a Fail. If box is a Fail, fn will receive the error contained within.

    Arguments

    • fn: (error?) -> any

    Returns Some | any

    orSome

    box.orSome(other)

    Returns the value contained within box if it is a Some, or returns other if it is a None or a Fail. This method ends the chain.

    Arguments

    • other: any

    Returns any

    some

    box.some()

    Returns the value contained within box. Throws if box is not a Some, so it's safer to use orSome. This method ends the chain.

    Returns any

    try

    box.try(fn)

    Attempts to call fn(value) and catches any error that occurs, returning a Fail with the error. Not executed if box is not a Some.

    Arguments

    • fn: value -> Some | None | Fail

    Returns Some | None | Fail

    unwrap

    box.unwrap(ifSome, ifNone, ifFail)

    Calls whichever function corresponds to the instance type and returns its value.

    Arguments

    • ifSome: value -> any
    • ifNone: () -> any
    • ifFail: error -> any

    Returns any

    see also

    • param.macro - Babel plugin for compile time partial application & lambda parameters

    contributing

    Pull requests and any issues found are always welcome.

    1. Fork the project, and preferably create a branch named something like feat-make-better
    2. Modify the source files as needed
    3. Make sure all tests continue to pass, and it never hurts to have more tests
    4. Push & pull request! 🎉

    license

    MIT © Bo Lingen / citycide

    Install

    npm i tryad

    DownloadsWeekly Downloads

    2

    Version

    1.0.0

    License

    MIT

    Last publish

    Collaborators

    • citycide
    • haltcase