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

Improve documentation #18

Open
2 of 5 tasks
paldepind opened this issue Dec 18, 2016 · 7 comments
Open
2 of 5 tasks

Improve documentation #18

paldepind opened this issue Dec 18, 2016 · 7 comments
Assignees

Comments

@paldepind
Copy link
Member

paldepind commented Dec 18, 2016

The documentation is currently pretty bad.

Add

  • Description of Stream
  • Description of Behavior
  • Description of Now
  • Better explanations
  • Examples
@paldepind paldepind self-assigned this Dec 18, 2016
@dmitriz
Copy link
Contributor

dmitriz commented May 5, 2017

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 :)

@paldepind
Copy link
Member Author

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

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 😭

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 👍

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?

@dmitriz
Copy link
Contributor

dmitriz commented May 6, 2017

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
Copy link
Contributor

dmitriz commented May 6, 2017

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?

@jethrolarson
Copy link

Having complete docs would really help in adoption.

@paldepind
Copy link
Member Author

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

@jethrolarson
Copy link

jethrolarson commented Jun 1, 2018 via email

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

3 participants