Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporate monads and category theory #94

Closed
paulmillr opened this issue Apr 10, 2013 · 256 comments
Closed

Incorporate monads and category theory #94

paulmillr opened this issue Apr 10, 2013 · 256 comments

Comments

@paulmillr
Copy link

Brian Mckenna criticised current spec. He proposes to use FP approach to achieve much better modularity.

Suggest to read it, really good ideas with just three changes.

http://brianmckenna.org/blog/category_theory_promisesaplus

His proposal is to incorporate into spec three simple apis:

  1. Promise.of(a) will turn anything into promise.
  2. Promise#then(f) should take one function, not two.
  3. Promise#onRejected(f): move onRejected to prototype instead of second arg.

edit: see promises-aplus/constructor-spec#24 for more discussion

@bergus
Copy link

bergus commented Apr 10, 2013

Yes, I really would like monadic promises as well…

Only I fear that won't happen. I already objected in #75 on the recursive assimilation process, but as it stands the then method is overloaded with join as well :-(

And that point function is subject of the (independent) resolvers-spec. As Brian already wrote, it would be sufficient in most cases to construct a simple thenable, instead of using some implementation-dependent Promise constructor.

@domenic
Copy link
Member

domenic commented Apr 10, 2013

Yeah this is really not happening. It totally ignores reality in favor of typed-language fantasy land, making a more awkward and less useful API just to satisfy some peoples' aesthetic preferences that aren't even applicable to JavaScript. It misses the point of promises (modeling synchronous control flow from imperative languages), albeit in a novel way from the usual misunderstandings.

It is also hilariously inaccurate, as the thenable described comes nowhere near satisfying the spec. My guess is that it would pass approximately one of the ~500 tests in our test suite.

Someone more diplomatic than me should probably chime in too.

@domenic domenic closed this as completed Apr 10, 2013
@briancavalier
Copy link
Member

@paulmillr, @bergus: I fully understand and appreciate this perspective, and similar ideas have been discussed at various lengths by the Promises/A+ contributors.

As @domenic has rightly pointed out, though, it isn't practical for Promises/A+ to go in the direction Brian Mckenna is proposing.

@puffnfresh
Copy link

@domenic you miss the point of monads. They implement exactly what you're describing in the post. They create sequential, synchronous control flow. That's why they're used in Haskell as IO! That's why promises are monadic!

Please point out where I'm missing the point of promises.

I'm basing the API off of category theory but it's for my aesthetic preferences!? When you say the API is "more awkward and less useful", that's reflecting your aesthetic preferences. Mine haven't even come into it!

I'm going to work on a separate specification - I'm hoping the rest of the JavaScript community will embrace it, rather than immediately reject it because it was based off of types.

@juandopazo
Copy link
Contributor

I'm going to work on a separate specification

@pufuwozu terrible idea. The reason why A+ isn't separating map and bind isn't because of aesthetic preferences. We didn't even discuss the issue because it's a breaking change and it came way too late into the discussion. then became a de facto standard that even the DOM is now likely to follow with DOMFuture.

@puffnfresh
Copy link

@juandopazo I don't really care about having bind fall back to map when it doesn't return a promise. I'd prefer it to throw an exception but I'll just be careful.

What I do care about is a specified way to create promises and onRejected to be separate from then. That's all.

@ForbesLindesay
Copy link
Member

A specified way to create promises is coming, it's just not here yet. see https://github.com/promises-aplus/resolvers-spec, which is nearly ready for a version 0.

@ForbesLindesay
Copy link
Member

Separating .then and .onRejected was not done for very good reasons:

  1. It allows for the parallel control flow of: Do Operation A, If operation A succeeded, do B, if operation A failed, do C. Separation makes that very hard to write.
  2. Keeping the API surface that needed to be implemented for interoperability to a minimum is critical to the success of promises. There are are many promises out there that don't come close to implementing this spec, but are still good enough for full interoperability because they have a .then method that takes two arguments: onFulfilled and onRejected.

@briancavalier
Copy link
Member

What I do care about is a specified way to create promises and onRejected to be separate from then. That's all.

@pufuwozu In that case, I would suggest you should join the discussion for creating promises rather than go out on your own. You could also propose a discussion to this group for an optional onRejected API (several promise implementations provide this already: when.js's promise.otherwise, Q's promise.catch, other libs provide promise.fail, etc.).

However, changing then simply is not an option.

@juandopazo
Copy link
Contributor

While it might be nice for some people to have a specified way of creating promises, it's certainly unnecessary. Different platforms will have different ways of creating them. This is already the case. For instance, see the WinJS promises vs DOMFuture. The point of having a spec is compatibility between them. And in that spirit, having a smaller API surface is better.

Creating a new specification just to add onRejected sounds a bit overkill.

@ForbesLindesay
Copy link
Member

Finally, the reason we didn't separate out bind and map is simply that it's not pragmatic to do so. It fits very nice with a theoretical computer science model of the world, but not with practical usage of the libraries. JavaScript is characterized by pragmatism over theory, and is ultimately devoid of type safety as a result. Without the benefits of type safety, separating out bind and map doesn't gain you anything.

It's not the aesthetics that @domenic or me or @briancavalier or anyone else like that matter; it's the aesthetics of the language itself. JavaScript's concept of a promise is subtly different to (although closely related to) the idea of a monad. It's the result of people attempting to use the ideas of a monad in real world applications, not the result of ignoring past work and coming up with something new from scratch.

@puffnfresh
Copy link

@ForbesLindesay I don't care very much about the separation of bind and map. As long as it's at least bind then I'll just be careful with the other behaviour.

... ideas of a monad in real world applications...

That's exactly what I want. Let's not implement partial monads that we can't abstract over. Let's recognise it for what it is and allow functions which work for ALL monads. Why not allow DRY code instead of some strange idea of aesthetics of JavaScript?

It might be too late but I'd rather a specification which allows DRY code with very partial backwards compatibility. I do not accept aesthetics of JavaScript as an excuse for developers to write more libraries that we can't abstract over.

@ForbesLindesay
Copy link
Member

We can abstract over them. There are hundreds of JavaScript libraries that use the promises and next to none that use traditional monad APIs.

You completely (deliberately?) missed the point of what I was saying about monads in real world applications. I'm saying people took the ideas of monads and used them in real world applications and adapted them to fit well with how JavaScript works. They are not, nor are they designed to be, interoperable with monads from Haskell. If you're creating a bridge for interop between the two languages, you could always automate that conversion.

They are very demonstrably DRY and possible to abstract over. If anything the .then method allows for more DRY code because you don't have to special case for whether the result is a promise or a non-promise value. Functions like Q.all demonstrate that they are a sufficiently powerful abstraction.

This is beginning to look like trolling, and if it continues to look like trolling, I will just be muting this thread. Without any interest int he aesthetics of JavaScript, I don't see your competing spec as anything likely to cause a problem.

@juandopazo
Copy link
Contributor

I don't see how you can't abstract over promises. You showed exactly how in your post. If we followed the DOMFuture API, it would look like this:

// the DOMFuture way of defining point()
function point(value) {
  return new Future(function (resolver) {
    resolver.accept(value);
  });
}
// Adding onRejected
Future.prototype.onRejected = function (f) {
  return this.then(null, f);
};

// flatMap is exactly the same if you don't want to separate bind and map
function flatMap(p, f) {
    return p.then(f);
}

Starting a standards war over this sounds like a great way to use our time.

@puffnfresh
Copy link

People misunderstood what I meant by "abstract over" - of course we can build libraries on top of the promises specification :)

I want to abstract over all monads. That's a very important thing for DRY code. I should be able to write code like this for example:

function liftA2(pa, pb, f) {
    return ap(pb, map(pa, function(a) {
        return function(b) {
            return f(a, b);
        };
    }));
}

// Promises (called when both succeed)
liftA2(readFIle('hello.txt'), readFile('world.txt'), function(hello, world) {
    console.log(hello + ' ' + world);
});

// Optional values (only inserted into database when both exist)
liftA2(optionalUsername, optionalPassword, function(username, password) {
    insertIntoTable("user", {username: username, password: password});
});

// Arrays (function called for each element of each array)
liftA2([1, 2, 3], [4, 5, 6], function(a, b) {
    console.log(a + b);
});

All I need to make the above work is for optional values and arrays to have a then and each constructor to have a point function. I don't have to write that function for each different monad - I only have to write it once. That's just one example of a function that works for any monad - there's many, many more.

I do care about the aesthetics for the API. I don't not accept aesthetics of JavaScript as an excuse to not allow me to do the above.

I care very much about generalised, DRY code. It's the right thing to do, even in JavaScript.

@Twisol
Copy link

Twisol commented Apr 10, 2013

It allows for the parallel control flow of: Do Operation A, If operation A succeeded, do B, if operation A failed, do C. Separation makes that very hard to write.

It's not just hard, it's impossible to get the same semantics. Promise has to be a bifunctor (i.e. has a bimap that takes two functions) for this to work at all. There are other bifunctors out there, such as Either, with either as its bimap.

@pufuwozu: I think the reason you're getting so much pushback on this, is that we create Promise APIs to make it easier for developers to write and manage asynchronous code. From my own experimentation in this area, you can only take the formalization so far in Javascript before the language starts to push back. The fmap/bind divide is one of those places; it's just much easier to use the library if you combine the two together.

You recall, perhaps, how electromagnetism and the weak force are actually part of the same mechanism, but only at very high temperatures? Well, we simply can't turn up the furnace that high in Javascript. Certain formalisms work beautifully in Haskell but fall flat in Javascript without support from the language. Haskell's static typing lets you offload a ton of work onto the compiler, but Javascript doesn't help much at all!

Prime example: in my own sandbox I've attempted to split Promise into two parts: a Result wrapping of try-catch, and a Future for deferred return values. It's theoretically nice until you realize one thing: monads don't naïvely compose. I don't know how the heck you would write monad transformers in Javascript. 😀

@puffnfresh
Copy link

@Twisol JavaScript is limited but we can still achieve DRY code by applying category theory. I do it: http://bilby.brianmckenna.org/ - the "but real world" excuse doesn't apply; I've used that library in a compiler!

Different monads don't compose but monad transformers are easy to implement in JS when you get to them ;)

@juandopazo
Copy link
Contributor

@pufuwozu can you show with a concrete example why you can't write the example code you posted with promises?

@ForbesLindesay
Copy link
Member

Your example of code that would be difficult to write with promises as they currently stand would in fact be easy to write for promises. I'm not 100% sure of the behavior of ap and map since you haven't included source code for them, but those two functions would (obviously) be different for promises vs. traditional monads. Since there are vastly more libraries using promises than monads, it is the monads that are preventing you from writing DRY code.

If you provide JavaScript source for ap and map using monads, I will write the corresponding functions for promises. Otherwise, stop trolling.

@kennknowles
Copy link

+1 to not starting a standards war. Any correct model of promises can be wrapped into a monadic interface, and it is pretty normal for standards to be a little off and require wrapping with a library.

Also, our understanding will improve over time, and it would be a disaster to have to move as slow as standards processes and have to wait for social trends to fall in line with mathematical truths. For example, before 2008 @pufuwozu would probably not have known to mention applicative functors.

-1 to the theory-phobia and accusations of trolling. Whether you like it or not, promises form a monad, and @pufuwozu is advocating that the standard expose this very useful structure. I understand why it will not, and agree somewhat with the decision, but that does not reduce the validity of the criticism.

@puffnfresh
Copy link

@ForbesLindesay you should take a look at my blog post:

function map(p, f) {
    return flatMap(p, function(a) {
        return point(f(a));
    });
}

function ap(p, pf) {
    return flatMap(pf, function(f) {
        return map(p, f);
    });
}

So we need a both a point and flatMap which works for Arrays, optional values, promises and anything else monadic. Then we can derive lots of useful DRY functions, like the liftA2 function that I posted above.

There will be no specified way to do this. That's what my blog post should have shown.

Thanks @kennknowles.

@Twisol
Copy link

Twisol commented Apr 10, 2013

Leaving aside the issue of the spec and whether or not we should change it (my opinion: we shouldn't), I'd be interested in seeing a pure approach to Promises. Something doesn't have to conform to a spec to be useful, and if it turned out that we're all wrong - that you can write pure code without it being brittle in the face of dynamic typing - then there's no reason inspiration couldn't be taken from the approach.

I don't want to see a standards war or any division of effort; just understand that there are reason that the spec is the way it is, and we need more than an impassioned plea for purity to make changes.

@ForbesLindesay
Copy link
Member

To clarify:

I'm not anti type theory per say. I actually find it extremely interesting. I've written a fair bit of code in languages like ML and F# that do a decent job of strong typing systems. I also think it would be an interesting experiment to attempt to create a strongly typed subset of JavaScript.

This is not the place for such experiments though, and my accusations of trolling are not directed at @paulmillr who made the very reasonable comment that we should consider category theory and monads more extensively. My accusation of trolling was leveled at @pufuwozu because he repeatedly tells us that the promises spec as it stands is insufficient for writing certain functions in a DRY way, yet refuses to demonstrate any actual problem.

@puffnfresh
Copy link

@ForbesLindesay I demonstrated the problem in my blog post. There is absolutely no refusal. It's an easily demonstrable problem.

@domenic
Copy link
Member

domenic commented Apr 10, 2013

I don't think there's any need to fear a "standards war." @pufuwozu can do his own thing, and I'm sure he'll have fun doing so, but I imagine the proportion of people using "monadises" versus Promises/A+ will be roughly equal to the proportion using Roy over ECMAScript.

@ForbesLindesay
Copy link
Member

The point function in your blog post is still fantastically broken.

@Raynos
Copy link

Raynos commented Apr 10, 2013

The problem is really simple.

Let's take three monads. Maybe, Promise and Array. I think we can all agree they support a point operation that takes a value and returns a monad and support a flatMap operation.

Now if I wanted to write a SINGLE polymorphic function that took two monads and did an operation on them. It would need a mechanism to look at my arguments and extract both a point and flatMap operation in a standardized way.

This isn't about writing DRY and composable functions with promises. It's about doing it with monads and accepting that a promise is one of hundreds of data types and tools in our toolbox. Sure we can just drink the promise coolaid and do everything with promises but that defeats the point of composability. Composability is trivial in a dictatorship.

@pufuwozu a reasonable suggestion is to write a flatMap and point function which just has a massive switch in the body that handles all real monads and all toy monads.

@puffnfresh
Copy link

@domenic that's fine. I usually hang out with the people that "do the right thing" - though, I actually thought my blog post would be taken seriously and appreciated.

@ForbesLindesay yeah, it's not easy to write a minimal implementation. Will I see a real one when you've managed implement the above?

@Raynos a switch would make the assumption that the world is closed and we could never write a new monad or use a new library. I would much prefer:

  • Promise.point = function() { /* ... */ };
  • Promise.prototype.constructor = Promise;

Which would allow us to approximate point for most functions.

@ForbesLindesay
Copy link
Member

Yep, the problem @pufuwozu is having is just that promises don't interoperate with monads. We already have a function that's broadly equivallent to how your blog post describes liftA2. We normally call it all and it takes promises, values, or arrays of promises/values in any combination and returns a promise for an array. You can see it implemented here.

To build "liftA2" on top of that:

function liftA2(arg, fn) {
  return all(arg).then(function (res) {
    return res.map(fn);
  });
}

Here is a minimal promise implementation. Most of the additional code is to handle the guarantees of asynchronous behaviour and polymorphism on return types.

As for the criticism of switches, you don't switch on library, you switch on type of object: e.g. promise, monad, array...

@kennknowles
Copy link

I love every additional comment here so many years later. I never muted this thread. The best part is that I have a lifetime of it to look forward to, for two reasons:

  1. The things done wrong here are fundamental to programming/computation itself, so there will never be a time when it is not wrong. And probably never be a time when it doesn't adversely effect programming in Javascript/Typescript.
  2. Those who think the conclusions here are OK will never search it up. This thread is kept alive by those who encountered the wrongness and searched the web for what went wrong.

So for those arriving now and forever: Welcome. You are in good company.

@davidchambers
Copy link

Like @kennknowles, I have never muted this thread. It had not occurred to me, though, that commenting here could provide pedagogical value. I now see this possibility.

Broken Promises† documents the problematic characteristics of promises: eagerness, cancellation, specialized API, error handling, and mixing exceptions with failures.

Please excuse the Medium link.

@dmitriz
Copy link

dmitriz commented Dec 21, 2019

https://www.reddit.com/r/programming/comments/ed7flb/functors_what_are_they/fbgjves/

The original Github issue on whether promises should be monadic is one of the most horrific examples of ignorance and closed-mindedness I have ever seen. #94

@friedbrice
Copy link

The specs are extended gutters and the gutters are full of mixed metaphors and when the weak metaphors finally break down, all the servers will crash. The accumulated filth of all their test suites and practical engineering will foam up about their waists and all the pragmatists and spec writers will look up and shout 'SAVE US!'...and I'll look down and whisper 'No.'

@AriaFallah
Copy link

Lmao @friedbrice instead of reviving an 8-year-old thread to live out your fantasies of not saving people from A+ promises, consider therapy

@friedbrice
Copy link

Joke's on you @AriaFallah! I already do therapy! XD

@Abastro
Copy link

Abastro commented Apr 1, 2021

How dumb lol. There are regions and niches each programming langs have, and ppl are deliberately breaking it. Also all the circlejerking. Kudos for the manager who closed this issue, well managed the circumstances.

@beezee
Copy link

beezee commented Apr 1, 2021

@Abastro yes very very dumb lolol. a sound mathematical model for computation is a "region and niche" for some computer programming languages. what "regions and niches" belong to the computer programming languages that don't care about a sound mathematical model for computation? landfills and early childhood education?

But I'm sure you are well versed in category theory and its application to programming, so your assessment is careful and well informed and not a knee-jerk anti-intellectual reaction to the scary idea that there's things you don't yet understand which might make you a better programmer.

@simpadjo
Copy link

simpadjo commented Apr 1, 2021

@beezee please let's not start a rant. This thread looks much better as it is: an old discussion, yearly snarky comments and a stream of github issue references that proves the point :)

@Abastro
Copy link

Abastro commented Apr 1, 2021

@beezee For one thing, CT does not apply well in CS(tbh it is embarrassing so much that it became a meme among mathematicians). For another, there are several computation models not one. Each language and environment may choose one over another, and using incompatible models at once would easily lead to mess-up. Lol

@beezee
Copy link

beezee commented Apr 1, 2021

@Abastro well you know what a monad is, I can't possibly imagine how you reason a Promise is not one, and just what sound model of computation you think is at play in the promises spec. The rest of your comment is noise.

@Abastro
Copy link

Abastro commented Apr 1, 2021

@beezee Basically, in JS we unroll structures like arrays including Promise as well. This allows more succint API for common usecases. This property of JS violates Functor property for good. Presence of exception in imperative settings is another problem for monadic representation. Really, what benefit you ever get from these CT theoretical bureaucracy in JS, a dynamic imperative language?
Stop trying to equate languages.

@emilypi
Copy link

emilypi commented Apr 2, 2021

This property of JS violates Functor property for good.

Are you telling me for-loops are broken by unrolling? What exactly do you think a functor is?

Presence of exception in imperative settings is another problem for monadic representation.

In the presence of exceptions, Javascript isn't a language. Do you believe addition is a poor abstraction because it is broken in the presence of exceptions? List concatenation? Data declaration? The new keyword? No? Then accept the fact that some people attempt to make their world more reasonable by working within a reasonable model. Even with these defects, I'm sure that algebraic models are useful enough to be included in the language. So too can CT.

There is absolutely nothing wrong with applying a partial model for what modicum of principled reasoning you can get in a fragment of the language that is reasonable to work with. Bringing CT to other languages has been wildly successful in many different ways, for many different types of languages, but it does not change your world just by existing. You can always opt in or opt out of using abstractions, but the general reasoning principles we use to guide our abstract reasoning can and should be sound, especially in situations like this. If you're balking at syntax, that's one thing, but don't sit there and tell me you're going to reject attempting to apply sound reasoning principles.

JS, a dynamic imperative language?

I've made a Monad crocheting a hat for a friend. I should hope JS can handle that level of complexity.

@tonymorris
Copy link

@beezee Basically, in JS we unroll structures like arrays including Promise as well. This allows more succint API for common usecases. This property of JS violates Functor property for good. Presence of exception in imperative settings is another problem for monadic representation. Really, what benefit you ever get from these CT theoretical bureaucracy in JS, a dynamic imperative language?
Stop trying to equate languages.

CT does not apply well in CS(tbh it is embarrassing so much that it became a meme among mathematicians).

This is both incorrect, in terms of programming, but also misrepresents mathematicians. Happy to help you learn why, any time.

yes, the ignoramus comments are getting old

@beezee
Copy link

beezee commented Apr 2, 2021

@emilypi i am anxiously awaiting a talk where you share the crocheting monad

@emilypi
Copy link

emilypi commented Apr 2, 2021

@beezee if you attended the talk on smash products, you would have seen me building riemannian manifolds out of paper and tape. This stuff can be either very big, or very smol. Even for the notoriously complex topos, you can build a very small one with three thumbtacks and some colored yarn (different colors, to differentiate arrows). Whether or not the arrangement means anything to you is in the quality of the observer, but a topos exists there in the thing nonetheless.

Same with monads. To deny that a thing is a monad is to deny its intrinsic structure. It's like denying clouds exist. When we say "this is a monad", we're not saying "we can build this thing into a monad with lots of effort and force", we are simply observing how it acts under certain observable preconditions (like no exceptions!), and that translates semantically into literally 2 behaviors: lifting a value into the thing, and multiplying via composition (m (m a) -> m a). For that reason, I really dislike jargon-ey conversations like this. If we just said "hey, promise has a nicer minimalist interface consisting of return and andThen which have nicer reasoning principles than the existing interface", it wouldn't be controversial. But as soon as anyone says "monads", everyone loses their goddamn minds. It's reactive to the jargon, not the thing.

@beezee
Copy link

beezee commented Apr 2, 2021

@emilypi I did catch that talk it was awesome. lol @ denying clouds exist, I love this take. Reminds me of one of my favorite Lambda Man moments where Phil Wadler talks about invention vs discovery

@tonymorris
Copy link

literally 2 behaviors

Is 2 a number and is it less than the number 3?

But as soon as anyone says "monads", everyone loses their goddamn minds. It's reactive to the jargon, not the thing.

In terms of pedagogy, I agree that there is a common and somewhat profoundly under-informed overreaction to the terminology "is a monad", which generally doesn't exist when we say that 2 is a "number", despite being just another observation of structure. I speculate that the reason for this is the practice of early education, where "number" becomes an obvious and accepted piece of jargon that we can all communicate successfully with each other.

I think there is a further push (frustration?), sometimes without success, to move the term "monad" (and other unfamiliar terminology) into a similar acceptance. Further, a tacit and optimistic suggestion of something like, "perhaps your mathematics education was halted at aged 91, but never mind, just catch up, I'll help!"

I think this is why you see a less-forgiving use of jargon in what started out as, "this not a mathematics learning session -- but rather, discussion on a programming API intended for common use; don't screw it up!"

Footnotes

  1. Lockhart's Lament

@MostAwesomeDude
Copy link

I recently wrote a small note explaining how, among other things, promises are not just monads, but "mothers of monads"; they identify codensity. As a result, it is not negotiable whether promises are monadic; it is only a choice of whether the language is ergonomic when handling monadic actions. While it is far too late for ECMAScript, perhaps other languages can benefit from hindsight.

@dmitriz
Copy link

dmitriz commented Feb 11, 2022

@MostAwesomeDude Your note is discussing the codensity monad but doesn't seem to mention promises at all. Then in a comment below you say "let's imagine promises as a functor". I don't quite understand what you mean by "imagine", but just for a reference, here is my StackOverflow post proving mathematically that Promise is neither a functor nor a monad: https://stackoverflow.com/a/50173415/1614973

@MostAwesomeDude
Copy link

@dmitriz: My note is not language-specific. Indeed, JS/ES promises do not form a functor, but this is a language-specific design defect. For example, Twisted Python's Deferreds are well-known to form a functor and monad, including @Twisol's conception of bifunctorality. (The oldest example I could find of somebody who is not me saying this is 15 years ago. (I suppose that this technically makes my post yet another instance of Twisted users pointing out issues with JS's event-loop design. (Sorry.)))

More pointedly, to understand where I ended up on all of this, note that Monte promises form a monad, and although I didn't intend it at the time, it is exactly the codensity monad for Monte's event loop. I chose to be more like E and Twisted, and less like ECMAScript and Node.js.

To make the link explicit: callback-oriented asynchronous continuations are an example of a codensity monad, or "mother of all monads"; ergonomic expression of a codensity monad leads to ergonomic expression of many other monads too. This is what @puffnfresh is really driving at achieving, since there are typically only a few builtin codensity monads per programming language. However, there is more than one, which invokes the spectre of colored functions; hence my original note.

@dmitriz
Copy link

dmitriz commented Feb 11, 2022

@MostAwesomeDude Yes, I was only referring to the JS promise spec - the subject of this repository. Thanks for showing other languages have better, more compliant implementations, demonstrating that it would have been totally possible to do it in JS, yet it wasn't done. What this shows through, every time you say the promise is a functor or monad, you need to qualify you are referring to other implementations, since JS one is not.

@Jerry1144
Copy link

Looking at this years old thread… I think maybe the problem is y'all failed to provide a concrete example for separating fmap and bind (ie map and flatMap) uses of Then()?

Earlier example argues that SomePromise.then(a => Promise b),then(Promise b => c) ought to be equivalent to SomePromise.then(a => c) because monad laws, or otherwise it's not DRY. Conservatives fails to see (and he failed to show, really, by using malformed a => Promise b and an Thenable b => c expecting that malformed promise/thenable) why ppl ever would like to map an a => Promise b onto a Promise at all. Breaking monad laws breaks this particular use case while keeping any sane uses intact, no?

So, maping instead of flatMaping an a => Promise b is only because of you later map Promise b => a onto the same thing, and monad laws guarantee you can just compute a => c instead. Years later do we have concrete examples for this now?

I'm a newbie, but Promise b => c looks terribly like signature for a Promise canceler, as this function appears to know beforehand what a given Promise would end up as, perhaps by looking at the constituents of the Promise. map'd into a PromiseArray it possibly immediately cancels other Promises and uses the return value of the first returning promise. Mapped into a Promise and maybe it cancels the whole promise chain up to the previous .thenMap(a => Promise b)?

Monad laws also guarantee you can transform every .then(a => c) into .thenMap(a => Promise b).thenMap(Promise b => c), but what use could such transformation possibly be?

PS: FP tutorials always assert monad laws be upheld without giving convincing examples why anyone or the interpreter need those certain good behavior. If GHC were transforming some fmap into fmap funcIntoSomeMonad . fmap SomeMonadCatamorphism as an optimization magic it would be better to be pointed out.

@puffnfresh
Copy link

@Jerry1144 code reuse

@emilypi
Copy link

emilypi commented May 12, 2022

Kuromi for your thoughts

signal-2021-12-25-110035

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests