Ramda chain usage

后端 未结 4 526
深忆病人
深忆病人 2020-12-16 02:19

From the documentation:

var duplicate = n => [n, n];
R.chain(duplicate, [1, 2, 3]); //=> [1, 1, 2, 2, 3, 3]
R.chain(R.append, R.head)([1, 2, 3]); //=&g         


        
4条回答
  •  佛祖请我去吃肉
    2020-12-16 02:42

    It is probably easier to first look at the abstract version of the R.chain for functions and to distinguish between functions m: r -> a treated as monads and functions f: a -> r -> b treated as Kleisli arrows, as mentioned in this answer.

    Then R.chain is defined as:

    // (a -> r -> b, r -> a) -> r -> b
    R.chain = (f, m) => x => f(m(x))(x)
    

    This can be useful, when x is some kind of configuration parameter, the same for both f and m. Then a = m(x) is the value returned by m for that parameter, and g = f(_)(x) is the function returned by f for the same parameter. Think of x as some kind of environment that goes into both m and f. Then the above definition can be broken down as:

    R.chain = (f, m) => x => {
        const a = m(x)
            , g = a => f(a)(x)
        return g(a)
    }
    

    In comparison, the R.map for functions corresponds to the case when f is independent of that parameter x:

    // (a -> b, r -> a) -> r -> b
    R.map = (f, m) => x => f(m(x))
    

    which, of course, is the usual function composition from the outside.

    Another conceptual approach to define chain (aka bind in Haskell) is to apply map (aka fmap) followed by flatten (aka join).

    R.chain = (f, m) => {
        // m1: r -> r -> b
        const m1 = R.map(f, m)
        // flattening to fm1: r -> b
        const fm1 = x => m1(x)(x)
        return fm1
    }
    

    Now for m = x => R.head(x) and f = a => x => R.append(a)(x), R.chain(f, m) is equivalent to putting the parameter x into both f and m and composing the results:

    x => R.append(R.head(x))(x)
    

    which gives the expected result.

    Warning. Note that the R.append function here must be curried as it represents the Kleisli arrow a -> r -> b. Incidentally, Ramda provides the same named function also as uncurried, but it is the curried one used here. To see this, let us get our custom uncurried R.append:

    const appendCustom = (a, b) => R.append(a, b)
    

    Then note how Ramda's REPL throws an error and gives unexpected result:

    // Define our own uncurried append
    const appendCustom = (a, b) => R.append(a, b)
    R.chain(appendCustom, R.head)([1, 2]);
    // => t(...) is not a function
    

    http://ramdajs.com/repl/?v=0.25.0#?%2F%2F%20Define%20our%20own%20uncurried%20append%0Aconst%20appendCustom%20%3D%20%28a%2C%20b%29%20%3D%3E%20R.append%28a%2C%20b%29%0AR.chain%28appendCustom%2C%20R.head%29%28%5B1%2C%202%5D%29%3B%0A

    What really happens here, appendCustom is executed in its curried form: appendCustom(a)(b), with the second call is delegated to some internal function returning the error.

提交回复
热议问题