Haskell Monad bind operator confusion

后端 未结 3 1068
滥情空心
滥情空心 2021-01-30 16:53

Okay, so I am not a Haskell programmer, but I am absolutely intrigued by a lot of the ideas behind Haskell and am looking into learning it. But I\'m stuck at square one: I can\'

3条回答
  •  不要未来只要你来
    2021-01-30 17:20

    The article you link is based on sigfpe's article, which uses a flipped definition of bind:

    The first thing is that I've flipped the definition of bind and written it as the word 'bind' whereas it's normally written as the operator >>=. So bind f x is normally written as x >>= f.

    So, the Haskell bind takes a value enclosed in a monad, and returns a function, which takes a function and then calls it with the extracted value. I might be using non-precise terminology, so maybe better with code.

    You have:

    sine x = (sin x,     "sine was called.")
    cube x = (x * x * x, "cube was called.")
    

    Now, translating your JS bind (Haskell does automatic currying, so calling bind f returns a function that takes a tuple, and then pattern matching takes care of unpacking it into x and s, I hope that's understandable):

    bind f (x, s) = (y, s ++ t)
                    where (y, t) = f x
    

    You can see it working:

    *Main> :t sine
    sine :: Floating t => t -> (t, [Char])
    *Main> :t bind sine
    bind sine :: Floating t1 => (t1, [Char]) -> (t1, [Char])
    *Main> (bind sine . bind cube) (3, "")
    (0.956375928404503,"cube was called.sine was called.")
    

    Now, let's reverse arguments of bind:

    bind' (x, s) f = (y, s ++ t)
                     where (y, t) = f x
    

    You can clearly see it's still doing the same thing, but with a bit different syntax:

    *Main> bind' (bind' (3, "") cube) sine
    (0.956375928404503,"cube was called.sine was called.")
    

    Now, Haskell has a syntax trick that allows you to use any function as an infix operator. So you can write:

    *Main> (3, "") `bind'` cube `bind'` sine
    (0.956375928404503,"cube was called.sine was called.")
    

    Now rename bind' to >>= ((3, "") >>= cube >>= sine) and you've got what you were looking for. As you can see, with this definition, you can effectively get rid of the separate composition operator.

    Translating the new thing back into JavaScript would yield something like this (notice that again, I only reverse the argument order):

    var bind = function(tuple) {
        return function(f) {
            var x  = tuple[0],
                s  = tuple[1],
                fx = f(x),
                y  = fx[0],
                t  = fx[1];
    
            return [y, s + t];
        };
    };
    
    // ugly, but it's JS, after all
    var f = function(x) { return bind(bind(x)(cube))(sine); }
    
    f([3, ""]); // [0.956375928404503, "cube was called.sine was called."]
    

    Hope this helps, and not introduces more confusion — the point is that those two bind definitions are equivalent, only differing in call syntax.

提交回复
热议问题