Functor broken #1

Closed
puffnfresh opened this Issue May 23, 2013 · 8 comments

2 participants

@puffnfresh
  • u.map(function(a) { return a; })) is equivalent to u (identity)

Breaks for this example:

p.concat(p2).concat(p3).map(function(a) { return a; });
@mudge
Owner
@puffnfresh

You shouldn't make map always take an array. That's not a restriction that Fantasy Land imposes. Fantasy Land's is much more relaxed:

map :: f a -> (a -> b) -> f b

(i.e. you should be able to pass any function that takes an a and returns a b)

The best option I can see is making map run f over each of the resolved values.

@mudge mudge was assigned May 24, 2013
@mudge
Owner

I've been experimenting with this a little more; an issue is what it means to concat two Promises. At the moment, I have it like so:

concat :: Promise a -> Promise b -> Promise [a b]

But this means that, strictly, speaking, concating several promises in a row results in:

p.concat(p2) //=> Promise [a b]
p.concat(p2).concat(p3) //=> Promise [[a b] c]
p.concat(p2).concat(p3).concat(p4) //=> Promise [[[a b] c] d]

While consistent, my original use case was to have a way to combine multiple promises into one that only yields when all values are resolved. You would then need to make those values available and this interface was very nice to use:

promisedHttpRequest.concat(anotherPromisedHttpRequest).map(function (response, anotherResponse) {
    // Do something with the two response bodies.
});

As you say, this violates the Functor interface so I wanted to change it to be compliant:

promisedHttpRequest.concat(anotherPromisedHttpRequest).map(function (responses) {
    var response = responses[0],
        anotherResponse = responses[1];

    // Do something with the two response bodies.
});

However, the current definition of concat makes this surprising beyond two promises:

promisedHttpRequest.concat(anotherPromisedHttpRequest).concat(yetAnotherPromisedHttpRequest).map(function (responses) {
    var response = responses[0][0],
        anotherResponse = responses[0][1],
        yetAnotherResponse = response[1];

    // Do something with the two response bodies.
});

Perhaps defining the result of concat as strictly Promise [a b] is wrong as it would be nice have (Promise a) concat (Promise b) concat (Promise c) -> Promise a b c or perhaps I am abusing map in my above examples?

@puffnfresh

Ah, concat is broken. It should like this:

concat :: Promise a -> Promise a -> Promise a
@mudge
Owner

So it's only possible to concat things that can themselves be concatenated (e.g. arrays, etc.)?

@puffnfresh

@mudge that's one solution, or you could say that Promises always represent multiple values (of the same type) and then concat them internally.

So Promise could be something like an async Array, I guess.

@mudge
Owner
@mudge mudge closed this in 1fbbe8b May 24, 2013
@mudge
Owner

I've just pushed some changes to the interface to bring it back in line with the specification, please feel free to review and let me know if there are any issues.

I added a test explicitly for the case mentioned (identity of a semigroup): https://github.com/mudge/pacta/blob/master/test/pacta_test.js#L131-L142

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment