-
Notifications
You must be signed in to change notification settings - Fork 165
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
Comments
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 And that |
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. |
@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. |
@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. |
@pufuwozu terrible idea. The reason why A+ isn't separating |
@juandopazo I don't really care about having What I do care about is a specified way to create promises and |
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. |
Separating
|
@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 However, changing |
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 |
Finally, the reason we didn't separate out 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. |
@ForbesLindesay I don't care very much about the separation of
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. |
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 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. |
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. |
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:
All I need to make the above work is for optional values and arrays to have a 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. |
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 @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. 😀 |
@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 ;) |
@pufuwozu can you show with a concrete example why you can't write the example code you posted with promises? |
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 If you provide JavaScript source for |
+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. |
@ForbesLindesay you should take a look at my blog post:
So we need a both a There will be no specified way to do this. That's what my blog post should have shown. Thanks @kennknowles. |
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. |
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. |
@ForbesLindesay I demonstrated the problem in my blog post. There is absolutely no refusal. It's an easily demonstrable problem. |
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. |
The point function in your blog post is still fantastically broken. |
The problem is really simple. Let's take three monads. Maybe, Promise and Array. I think we can all agree they support a 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 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 |
@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:
Which would allow us to approximate |
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 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... |
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.' |
Lmao @friedbrice instead of reviving an 8-year-old thread to live out your fantasies of not saving people from A+ promises, consider therapy |
Joke's on you @AriaFallah! I already do therapy! XD |
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. |
@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. |
@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 :) |
@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 |
@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. |
@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? |
Are you telling me
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 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.
I've made a Monad crocheting a hat for a friend. I should hope JS can handle that level of complexity. |
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 |
@emilypi i am anxiously awaiting a talk where you share the crocheting monad |
@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 ( |
@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 |
Is 2 a number and is it less than the number 3?
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 |
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. |
@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 |
@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. |
@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. |
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 So, I'm a newbie, but Monad laws also guarantee you can transform every 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 |
@Jerry1144 code reuse |
JavaScript was shit because it was created in haste, yet it continues to be shit because the devs be stubborn for the sake of stubborn. |
lol in the world of async/await inside try/catch it sure feels like the point of promises "(modeling synchronous control flow from imperative languages)" as domenic puts it, is well achieved without having then take two arguments. and way BETTER achieved in this way. |
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:
Promise.of(a)
will turn anything into promise.Promise#then(f)
should take one function, not two.Promise#onRejected(f)
: moveonRejected
to prototype instead of second arg.edit: see promises-aplus/constructor-spec#24 for more discussion
The text was updated successfully, but these errors were encountered: