briancavalier / creed

Sophisticated and functionally-minded async with advanced features: coroutines, promises, ES2015 iterables, fantasy-land
https://briancavalier.github.io/creed
MIT License
273 stars 20 forks source link

more accessible documentation? #178

Open donhatch opened 6 years ago

donhatch commented 6 years ago

I have trouble taking native/A+ promises seriously, mostly due to its impossible-to-analyze behavior around automagically unwrapping thenables (I believe this is commonly known as "failure to be a monad").

As I consider long-term solutions, alternatives like fantasy-land and creed::async become quite attractive. But, frustratingly, my several attempts to dive into either one of them have failed, due to my failure to being able to follow the documentation of either, to any significant depth. I also have the feeling that, even if I succeed in vaulting the very high bar to entry, I will leave my normal friends and coworkers behind, since they will be unable to ever read my code from then on.

This creed::async certainly sounds great, from its introductory blurb (great marketing!), so I'd really like to try it, so that's what brings me here.

As I continue reading the README, I encounter:

What I'm trying to say is... is there any chance of getting some introductory material in there, to bridge the large gap between the shallow "Try It" section and the deep "API" section, for programmers like me who want to learn this library?

In particular, I'd be interested in some dedicated material explaining, in both plain language and code:

A final note-- personally, I know Promises/A+ already, so I don't mind if the doc assumes that knowledge, for my own use in learning the library. However, if this library is intended to be a serious option for the world for doing async, it would be great if there were also an introduction from first principles for javascript programmers, so that people can skip A+ and get right to the sane stuff. That would be a more ambitious undertaking, though.

Frikki commented 6 years ago

Let me attempt to address.

I have trouble taking native/A+ promises seriously

Okay.

mostly due to its impossible-to-analyze behavior around automagically unwrapping thenables (I believe this is commonly known as "failure to be a monad").||

Belief belongs to a concept called religion.

As I consider long-term solutions, alternatives like fantasy-land and creed::async become quite attractive.

Okay.

But, frustratingly, my several attempts to dive into either one of them have failed, due to my failure to being able to follow the documentation of either, to any significant depth.

What could be bettered?

I also have the feeling that, even if I succeed in vaulting the very high bar to entry, I will leave my normal friends and coworkers behind, since they will be unable to ever read my code from then on.

The tragedy of the commons. If your co-workers cannot understand your code, then write it so they can. If you cannot do that, the library doesn’t apply to the level of your co-workers. Not everybody understands Haskell either.

The fantasy-land doc dives straight into algebraic "abstract nonsense" (I don't neceessarily use the term dismissively, but it does make that doc impenetrable and thus not useful to me for learning).

Abstract nonsense? Since when was abstract nonsense? Is category theory, for example, nonsense? After all, it’s abstract as f**k.

fluture-js/Fluture might be a decent alternative; I'll explore that in parallel.

I agree. You should. It’s good to be an explorer. 42 is not the answer. O, well ...

This creed::async certainly sounds great, from its introductory blurb (great marketing!), so I'd really like to try it, so that's what brings me here.

I had the same feeling about JavaScript.

he async-problem example. Ok, I guess it solves this puzzle neatly. Can't really make head nor tail of the details at this point, and it's doesn't seem that relevant to my wish to learn the library.

Okay. What would be relevant?

The "Try it" section. Ok, I follow this section: it looks just like the Promises I know, so far, but there's not a lot in this section.

Fair enough. What should be added?

... scrolling down, hoping to see some more introductory examples of how to use various features, and I reach: API : Run async tasks: coroutine :: Generator -> (...* --> Promise e a) : Create an async coroutine from a promise-yielding generator. [... example that doesn't seem relevant to me yet ...] WHOA! I suddenly feel very far out of my depth...

Keep swimming. Maybe a submarine is a good example? Better yet, what are you looking for?

fromNode :: NodeApi e a --> (...* --> Promise e a) [... more stuff that doesn't seem relevant to me at all ...]

I once went to a dating site, and ... well, same.

scrolling down more, I see lots more API reference and code examples, but they all seem to assume I already know what's going on. I'm lacking any context or feeling like I know how to get my foot in the door.

You’re looking for basics?

What I'm trying to say is... is there any chance of getting some introductory material in there, to bridge the large gap between the shallow "Try It" section and the deep "API" section, for programmers like me who want to learn this library?

I belive that could be improved. Likely an article addressing your particular use case.

I have to stop here because I am getting tired reading the flood of text that is mostly a complaint about communication not fitting a particular purpose. I’d love to address your concerns later ... maybe after your next post.

Avaq commented 6 years ago

the flood of text that is mostly a complaint about communication not fitting a particular purpose

I don't think @donhatch is complaining, I think they are raising valid concerns about the accessibility of the alternative Promise libraries. You might be too hard on them in your response, @Frikki. I think most of what @donhatch said stems from confusion.

I have trouble taking native/A+ promises seriously, mostly due to its impossible-to-analyze behavior around automagically unwrapping thenables (I believe this is commonly known as "failure to be a monad").

This is exactly the kind of complaint that we would expect from the target audience of alternative Promise libraries. Using Creed, Fluture, Folktale, or similar is indeed a way to avoid having to deal with the absurdity of the native Promise implementation.

As I consider long-term solutions, alternatives like fantasy-land and creed::async become quite attractive.

This seems to highlight the first points of confusion: that Fantasy Land is an alternative to native Promise/A+ somehow.

You don't need to look at Fantasy Land if you want an alternative to Promises/A+. Fantasy Land only exists to acknowledge within the JS community that some constructs can be related to category theory, and made to interoperate because of that. Fantasy Land describes the association with category theory (hence the abstract nonsense) and an interface for independent libraries to be able to use each others algebraic operations.

As I continue reading the README, I encounter [a lot of in-depth material] What I'm trying to say is... is there any chance of getting some introductory material in there, to bridge the large gap between the shallow "Try It" section and the deep "API" section, for programmers like me who want to learn this library?

The problem you're running into is that a lot of these documentations assume understanding of underlying concepts. These concepts are broad, and too much for every individual library to cover, but they can also be very widely applied. A great place to start is the Mostly Adequate Guide. Many of the concepts covered there can be extrapolated to understand the documentations of functionally minded libraries.

My impression [...] is that a crucial motivating point about this library is something like [...] "it has a [Promises/A+] compliant then, but if you want something with analyzable semantics, don't use that, use its chain instead"

That's right. Promises/A+ is another spec drafted with interoperability in mind, and for the benefit of interoperability, the people who drafted the spec arbitrarily decided upon these strange assimilation semantics you've mentioned before. In order for Promises to interoperate, the then function must behave strangely. The Native Promise implementation (and Bluebird, and most others) took it one step further though, and added the same behaviour to all Promise functions (including all of its constructors), making it so there is no escape from the strange semantics. Creed is different in that it has the minimum necessary to be spec compliant, but it also has sane alternatives to the then function. You as a user are encouraged to use the sane alternatives (like map and chain) and leave then as a means for interoperability and colleagues not willing to make the leap. As an added benefit, map and chain and friends are compliant with the Fantasy Land spec, meaning that interoperability with libraries such as Ramda is also supported.

A dedicated "Motivation" section might go a long way.

I think that's a good suggestion.

if this library is intended to be a serious option for the world for doing async, it would be great if there were also an introduction from first principles for javascript programmers

I see where you are coming from, but as I mentioned, the underlying principles are shared between all functionally minded libraries and are a lot to cover. Once you are familiar with the basics I assure you all of these documentations will make a lot more sense! In the Fluture documentation, I try to link to material on the basics whenever necessary. It's my best idea for guiding new users. Perhaps Creed can do the same. How do you think it can be improved?

donhatch commented 6 years ago

@Frikki Although there are apparently several questions in your reply, I don't see anything that looks like it really wants an answer. All of your "what could be better?" "what would be relevant?" "you're looking for basics?" questions are things I did attempt to address in my original post and even in the parts of it that you quoted, and I don't have the sense that I would make anything better by repeating those parts.

I hear that you perceive much of my post as complaining and as a wall of text half of which you didn't read. Okay.

Regarding whether category theory is "abstract nonsense"... yes. Again, I don't use the term to be dismissive or derogatory; I mean it in the sense well-described in the Background section of the linked wikipedia article, which agrees with how my category theory professor used the term.

donhatch commented 6 years ago

@Avaq Thank you! Your reply was incredibly helpful to me, as it clarifies quite a lot of what I was wondering about and was unsure of about creed::async. In fact, as I look it over again, I get the sense that the entire text of your reply, if it were simply dropped into an early part of the README with minor edits, would serve fantastically as exactly the motivational and contextual bridge that I was missing.

Yes, I find the Fluture documentation to be quite accessible; things like your links to basics contribute well to that, as do your many comments and cross-links from one concept to related concepts within the doc itself. My sense is that if the creed::async API doc could be filled out a bit with such comments and cross-links, it would become similarly accessible.

For one very specific example: in the API section, I'd get benefit from seeing "then" and "chain" cross-reference each other with something like "chain is then without the automatic unwrapping". And many similar things like that.

donhatch commented 6 years ago

@Avaq Just to clarify this particular point...

if this library is intended to be a serious option for the world for doing async, it would be great if there were also an introduction from first principles for javascript programmers

I see where you are coming from, but as I mentioned, the underlying principles are shared between all functionally minded libraries and are a lot to cover. Once you are familiar with the basics I assure you all of these documentations will make a lot more sense! In the Fluture documentation, I try to link to material on the basics whenever necessary. It's my best idea for guiding new users. Perhaps Creed can do the same. How do you think it can be improved?

Sure, I'm not suggesting writing another introduction to common introductory material that exists elsewhere-- I agree that links to such (with an appropriate amount of explanation of why they are being linked and how they relate to the current material), serve quite well for that.

When I said "from first principles" (probably a poor choice of words), I meant specifically "without depending on Promises/A+ documentation". Promises/A+ serves a necessary purpose at the moment, but, in an ideal future, Promises/A+ will be forgotten and more tenable projects like this one will live on, so it would be good to avoid dependencies on Promises/A+ docs (other than perhaps for historical context-- not for concepts).

dmitriz commented 6 years ago

@donhatch

I have trouble taking native/A+ promises seriously, mostly due to its impossible-to-analyze behavior around automagically unwrapping thenables (I believe this is commonly known as "failure to be a monad").

In fact, Promise is not even a Functor. And sure enough, you better not use any then prop anywhere inside your objects ;)

As I consider long-term solutions, alternatives like fantasy-land and creed::async become quite attractive.

Fantasy-land is not an alternative, it is not even a library. It is more of a collection of densely written and opinionated specs aimed at library writers. It does assume familiarity with many concepts it mentions and does not aim to provide any introduction nor give examples. Static Land is perhaps more user-friendly, it is still a spec though.

For something more accessible, in addition to the excellent "Professor Frisby's Mostly Adequate Guide to Functional Programming" mentioned by @Avaq, have a look at the practical examples in https://github.com/dmitriz/functional-examples.

Creed is entirely different, it is an actual library.

The async-problem example. Ok, I guess it solves this puzzle neatly. Can't really make head nor tail of the details at this point, and it's doesn't seem that relevant to my wish to learn the library.

It is true that adding some more explanations to this example would not hurt. @briancavalier is very friendly and open to suggestions, so feel free to make some.

The "Try it" section. Ok, I follow this section: it looks just like the Promises I know, so far, but there's not a lot in this section.

It does show some good features to get started, as well as the friendly REPL inspector facility. The use of map there is actually quite interesting:

> all([1, 2, 3].map(resolve))
Promise { fulfilled: 1,2,3 }

and perhaps not "too introductory" as it simultaneously uses 2 new operators. To get some better feeling, it always helps to play with those in isolation:

> creedPromise = resolve('hello')
Promise { fulfilled: hello }
> creedPromise1 = creedPromise.map(str => str + '?')
Promise { pending }
> creedPromise1
Promise { fulfilled: hello? }
> wrappedPromise = creedPromise.map(resolve)
Promise { pending }
> wrappedPromise
Promise { fulfilled: [object Promise { fulfilled: hello }] }
> together = all([creedPromise, creedPromise1])
Promise { fulfilled: hello,hello? }
// WAT? Ok, that one was a bit cryptic, so let's test it ...
> together.map(console.log)
Promise { pending }
> [ 'hello', 'hello?' ]

So we see that map is more atomic than then in that it does not run any unwrapping "magic". And all seems to run an array of promises in parallel, similar to Promise.all:

> native = Promise.all([creedPromise, creedPromise1])
Promise {
   ...  (mostly useless information from the native promise inspector)
> native.map(console.log)
TypeError: native.map is not a function
> native.then(console.log)
   ... 
> [ 'hello', 'hello?' ]

... scrolling down, hoping to see some more introductory examples of how to use various features, and I reach: API : Run async tasks: coroutine :: Generator -> (...* --> Promise e a) : Create an async coroutine from a promise-yielding generator. [... example that doesn't seem relevant to me yet ...] WHOA! I suddenly feel very far out of my depth...

Indeed, this surely is an advanced one, outside the core methods, not clear why it comes first...

fromNode :: NodeApi e a --> (...* --> Promise e a) [... more stuff that doesn't seem relevant to me at all ...]

That one is actually more basic, demonstrating how to turn a method from NodeApi into a creed promise:

> let {fromNode} = require('creed')
undefined
> readFileToCreedPromise = fromNode(fs.readFile)
[Function: promisified]
> readFileToCreedPromise('someFile', 'utf8')
Promise { pending }
> Error: ENOENT: no such file or directory, open 'someFile'

What I'm trying to say is... is there any chance of getting some introductory material in there, to bridge the large gap between the shallow "Try It" section and the deep "API" section, for programmers like me who want to learn this library?

It is true that an "Introduction" section aimed at "ordinary promise users" is missing.

In particular, I'd be interested in some dedicated material explaining, in both plain language and code: ...

These are excellent suggestions. An introductory section "Why would I use creed?" for regular promise users would certainly lower the access bar. Maybe start with some scary "then-able" surprises and move on showing how things are more transparent and easier to reason about with the sane map and chain methods resting on years of research in computer science and mathematics.

unscriptable commented 6 years ago

Maybe start with some scary "then-able" surprises and move on showing how things are more transparent and easier to reason about with the sane map and chain methods resting on years of research in computer science and mathematics.

This. :100:

bergus commented 6 years ago

Maybe start with some scary "then-able" surprises and move on to …

I would not recommend to start a tutorial with that. New users want to learn how to use the Creed library, not hear our opinions about why then is horrible. Sure, it should be mentioned somewhere, but not at the beginning.

briancavalier commented 6 years ago

Hi everyone. Thanks for the discussion. I'm sorry I've not been able to respond until the weekend (my time lately is very limited during the week)

I think there are some very good suggestions in here, and I'd like to try to focus subsequent discussion around those. Hopefully we can get to some tangible goals and start to turn them into concrete doc improvements.

Here are the things I saw mentioned that seem like they would be helpful improvements:

  1. Add simple "Introduction" and/or "Why would I use creed" sections
  2. Add another 1-2 simple examples to the README.
  3. Add links to helpful information about foundational functional programming concepts, such as the Mostly Adequate Guide.
  4. Perhaps more clearly encourage exploration via REPL.

I like all of these ideas, and I'm open to doing them.

If creed moves forward with these, I'd love help. As I mentioned, my time has been very limited over the past few weeks, and that's likely to continue for at least another 1-2 weeks. Discussion is helpful, and of course PRs would also be helpful.

dmitriz commented 6 years ago

@bergus

Maybe start with some scary "then-able" surprises and move on to …

I would not recommend to start a tutorial with that. New users want to learn how to use the Creed library, not hear our opinions about why then is horrible. Sure, it should be mentioned somewhere, but not at the beginning.

I actually would find it helpful.

My first questions, when seeing a new library, are not "How to use it?" but "Why use it?", "What it brings to the table?" and "What it does that X and Y don't do?

In this particular case, we have a library seemingly similar to the native Promise yet with at least twice as many methods that can be easily overwhelming for a newcomer. It clearly serves a wide range of people's expertise levels and relies on years of experience, but that also makes it harder for newcomers to answer those questions.

Concerning .then, it is actually wonderful in that it manages to do so much under the single method namespace. 😄 However, that ease and simplicity come at price of risks entailed in the whole design as e.g. is beautifully described in the Medium post https://medium.com/@avaq/broken-promises-2ae92780f33 by @Avaq. The problem is, such posts are rare, whereas the "glowing praise" posts are numerous that often skip the hard truth. But people who rely on this design for their work, deserve to know all sides, not only the shiny ones.

Without knowing that other side of equation, native promises look wonderfully easy to use and "working just fine" and "being used by everyone without problems", so why making life harder by "pleasing some few category theory sadists" 😄 I have not made it up, that is really what I've seen written even by experienced programmers. To which I can't think of any better response than by @tjaskula in his highly educational Medium article Can programming be liberated from the von Neumann style?:

In the end the popularity might be an evidence of minimum level of quality acceptable by the majority of people.

I understand it has some deeper roots in certain unfortunate education culture, that we obviously can't fix here. What we can do though, is explain how and where that knowledge can be practically beneficial in the specific use cases that Creed is addressing.

As for well-motivated opinions by people with experience, these are actually quite helpful to newcomers and great way to learn quickly and save a lot of time. As any other library, this one is based on certain opinions of the people who designed it, and knowing and understanding those is actually incredibly helpful, specifically when using the library.

briancavalier commented 6 years ago

In addition to these:

dmitriz commented 6 years ago

@briancavalier

it seems helpful for the API docs to emphasize map, et al. and group A+ / ES interop functions (i.e. then, catch, and finally) together and explain more clearly that's their purpose.

In this direction, it would be good to have some very basic but realistic examples. (My definition of "basic" is strictly one method from the library, with strict focus on that method with any possible distraction/complication removed.)

I will look into rewriting the Task-based examples into creed-based in https://github.com/dmitriz/functional-examples and perhaps adding some more. With the idea that the best ones can be later imported here.

Any PR to https://github.com/dmitriz/functional-examples or https://github.com/dmitriz/monadic-libraries-examples is of course welcome ;)

dmitriz commented 6 years ago

Here are some basic usage examples: https://github.com/dmitriz/functional-examples/blob/master/examples/12-creed.js https://github.com/dmitriz/functional-examples/blob/master/examples/13-creed-async.js https://github.com/dmitriz/functional-examples/blob/master/examples/22-creed-traversable.js

briancavalier commented 6 years ago

@dmitriz Thank you for putting together those examples!

I think the next step for this particular issue is actually to create a new issue that represents the concrete work to be done--from here and here, and potentially drawing from (and/or linking to) @dmitriz's examples.

Once again, thank you, everyone for the discussion and suggestions.

briancavalier commented 6 years ago

See #179

briancavalier commented 6 years ago

It turns out I was swamped for more than the 1-2 weeks I originally thought, but I just started working on the items in #179.