hareactive icon indicating copy to clipboard operation
hareactive copied to clipboard

Improve documentation

Open paldepind opened this issue 9 years ago • 7 comments

The documentation is currently pretty bad.

Add

  • [x] Description of Stream
  • [x] Description of Behavior
  • [ ] Description of Now
  • [ ] Better explanations
  • [ ] Examples

paldepind avatar Dec 18 '16 12:12 paldepind

Just wanted to share few thoughts.

  • It would be nice to give motivation with good examples for each concept or just provide a link.

  • Is the possible confusion with the Future e.g. in the sense of https://github.com/fluture-js/Fluture ?

A Stream is a list of futures.

Does it still apply to a live stream? With list we usually know all the values and can evaluate its length. But how can we evaluate the stream length if that can change?

apply<A, B>(behavior: Behavior<(a: A) => B>, stream: Stream<A>): Stream<B>

What about the other combinations - apply Stream to Behavior and Behavior to Behavior?

filter<A>(predicate: (a: A) => boolean, s: Stream<A>): Stream<A>

Can filter be possibly derived from a more general partial reduce, like the normal filter?

split<A>(predicate: (a: A) => boolean, stream: Stream<A>): [Stream<A>, Stream<A>]

Also here? That way there are fewer basic methods and others would be derived.

filterApply<A>(predicate: Behavior<(a: A) => boolean>, stream: Stream<A>): Stream<A>

Another partial reduce, of which filter is special case for the constant behavior?

keepWhen<A>(stream: Stream<A>, behavior: Behavior<boolean>): Stream<A>

That seems derived from applying the identity and filtering thereafter.

snapshot<B>(b: Behavior<B>, s: Stream<any>): Stream<B>

Not sure I understand this. Is that just like map into the identity?

  • Haven't looked at the others, but would be good to try keep the number of methods as small as possible or make some good hierarchy. Otherwise people may get scared away thinking of another RxJS :)

  • It is interesting how observables are totally absent :)

dmitriz avatar May 05 '17 12:05 dmitriz

You have definitely made a lot of really good observations! :smile:

It would be nice to give motivation with good examples for each concept or just provide a link.

I agree. The documentation is rather abstract as of now :sob:

Does it still apply to a live stream? With list we usually know all the values and can evaluate its length. But how can we evaluate the stream length if that can change?

Conceptually it does. In theory, we could know all past and future occurrences and represent them as a list. But in practice, we can't really do that.

What about the other combinations - apply Stream to Behavior and Behavior to Behavior?

I can't see how applying a stream to a behaviour makes sense. But for applying a behavior to a behavior there is the ap method on behaviors. It's unfortunately not in the documentation yet.

Can filter be possibly derived from a more general partial reduce, like the normal filter?

No. And strictly speaking the normal filter can't be derived from only reduce either. reduce gives a way to take structure apart. To implement filter you also need a way to assemble a new structure (like concat or cons).

Also here? That way there are fewer basic methods and others would be derived.

Yes. Definetly. split is just implemented like this:

export function split<A>(predicate: (a: A) => boolean, stream: Stream<A>): [Stream<A>, Stream<A>] {
  return [stream.filter(predicate), stream.filter((a) => !predicate(a))];
}

Another partial reduce, of which filter is special case for the constant behavior?

Yes. That is spot on.

That seems derived from applying the identity and filtering thereafter.

I hadn't thought of that. But I think you are right :+1:

Not sure I understand this. Is that just like map into the identity?

Semantically snapshot works like this:

function snapshot(behavior, stream) {
  return stream.map(({time}) => ({time, value: behavior(time)}));
}

For each occurrence in the stream we ask the behavior "what is your value right now?". And then a new stream is returned where the values in the first stream is replaced by the behavior's value at those points in time.

Haven't looked at the others, but would be good to try keep the number of methods as small as possible or make some good hierarchy. Otherwise, people may get scared away thinking of another RxJS :)

Yes. That is definitely a good idea. How do you think we should do that? Should we have a list of "essential" combinators and a list of "extra" combinators?

paldepind avatar May 06 '17 10:05 paldepind

You have definitely made a lot of really good observations! 😄

Thank you 😄

And thank you for your explanations.

What about the other combinations - apply Stream to Behavior and Behavior to Behavior?

I can't see how applying a stream to a behaviour makes sense. But for applying a behavior to a behavior there is the ap method on behaviors. It's unfortunately not in the documentation yet.

You can apply a stream of functions a->b to a behaviour of values a by returning the stream of evaluations at the moments when the stream emits. Basically "dual" to applying behavior of functions a->b to a stream of values a. Does it makes sense?

Haven't looked at the others, but would be good to try keep the number of methods as small as possible or make some good hierarchy. Otherwise, people may get scared away thinking of another RxJS :)

Yes. That is definitely a good idea. How do you think we should do that? Should we have a list of "essential" combinators and a list of "extra" combinators?

I think I would try to look from the perspective of the lazy pragmatic user. ;) Someone who needs to be convinced to use every next feature.

I think people are already convinced that streams are important, so we can start there. But you have already created the wonderful flyd library, raising few questions:

  • What this library does for streams that flyd cannot do?

  • Can it be used along with flyd?

  • If yes, which methods can be derived from flyd?

Then I would move to the "core" methods, first just for streams, preferably really a small number, none of which would be derivable from others and from flyd, and ideally each motivated with example how it makes like better. Having a fit with FL would also be great.

Next the derived convenience methods can follow. Like for example, http(obj) is core and various http.get(...) are derived.

I would think, ironically, a really small focused core may gain better appreciation than a longer one with possibly more work behind. :)

The thing with behaviors, it may take a while to some people to accept it. Many would just be interested in streams, so mixing streams and behaviours may scare them away. But then again, giving clear examples demonstrating the necessity may be convincing.

But the convincing part may not be easy, because people will try to do everything with streams, and maybe succeed with most of anything they need. For instance, you can model mouse movement or animation with some discrete stream based framing, where you get the behaviour property but you can still implement them with streams (like that library by James, https://github.com/JAForbes/pointer-stream). So more clear convincing implementation examples may be needed. Something like in Dr. Frisby course where the coding size gets down to 1/10. :)

These are just some brainstorming, mostly naive, that you can surely ignore as with your multiple widely adopted other libraries, I am sure you have other good ideas :)

dmitriz avatar May 06 '17 12:05 dmitriz

Can filter be possibly derived from a more general partial reduce, like the normal filter?

No. And strictly speaking the normal filter can't be derived from only reduce either. reduce gives a way to take structure apart. To implement filter you also need a way to assemble a new structure (like concat or cons).

Yes, I have meant it only for the arrays, with the concat - based reduce, I suppose that is what you mean.

Even for trees, that are foldable and traversable, it is not clear how to define the filter. But streams are analogous to arrays in many ways. More precisely, you can identify them with time-dependent arrays of the form

stream :: t -> [Pair t a]

So at every time t you have the array of pairs (time, value) with time <= t. Now, having this plain array, you have the usual reduce and hence the usual filter method until the time t (aka partial filter), which is derived from the partial reduce (via concat). And this is the "partial reduce" that I meant. And if you consider it for varying t, I think you get the filter.

Is there any possible flaw in this reasoning?

dmitriz avatar May 06 '17 14:05 dmitriz

Having complete docs would really help in adoption.

jethrolarson avatar May 31 '18 01:05 jethrolarson

Agreed @jethrolarson 😄 Are there anything in particular that you think lacks documentation?

paldepind avatar Jun 01 '18 09:06 paldepind

I looked at Future and it only shows two methods though looking at the source it provides monad interfaces and maybe more

On Fri, Jun 1, 2018, 2:44 AM Simon Friis Vindum [email protected] wrote:

Agreed @jethrolarson https://github.com/jethrolarson 😄 Are there anything in particular that you think lacks documentation?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/funkia/hareactive/issues/18#issuecomment-393830171, or mute the thread https://github.com/notifications/unsubscribe-auth/AAB-4BFg19uyKRSdazos-feIh0dCIY8Qks5t4QzhgaJpZM4LQHJq .

jethrolarson avatar Jun 01 '18 17:06 jethrolarson