Closed TrySound closed 5 years ago
Hi @TrySound. As it stands, I don't think we can mention either Plus or Foldable. I'll try to explain why for each.
If you look carefully at the type definition of Foldable:
reduce :: Foldable f => f a ~> ((b, a) -> b, b) -> b
you'll notice that given a function (b, a) -> b
, reduce
must be able to return a b
synchronously. That's impossible in the general case with an asynchronous data structure like a mostjs event stream. The best it can do in the general case is to return a future b
, iow a promise for b
, which violates Foldable.
Plus requires the type first to implement Alt:
A value that implements the Plus specification must also implement the Alt specification.
So, since mostjs doesn't currently implement Alt, it wouldn't be valid, for example, to alias empty() or never() to zero() and claim support for Plus.
That raises the question: could we implement Alt? The core team has discussed this, and if memory serves, we believe that there are at least two possible Alt implementations, each of which implies a different Plus implementation:
It's not clear which of these is the more useful and/or correct Alt/Plus. My most recent thinking has been that, since concat
was removed in @most/core
, it'd be reasonable to introduce Alt as option 2 above, with empty() as Plus zero(). But, honestly, it's still not entirely clear what is best.
Any thoughts on that? Do you see other options for a valid and useful Alt?
Was concat remove because of event time breaking? I like the idea of empty/continueWith pair
Was concat remove because of event time breaking?
Yeah, and it just has a misleading mental model. You can read more here: https://github.com/mostjs/core/pull/80
So do we leave plus and alt here?
Yeah, I'm up for adding them. I'd like to hear what @TylorS, @davidchase, and @Frikki think about it.
If we add them, this PR will need a few more todos:
Stream.zero = empty
,If we all agree this is the right direction, then I'll be happy to help.
After some offline discussion, I think we're good to go on this. @TrySound, are you up for handling the todos above?
Sure.
Awesome, thank you!
@briancavalier What about monoid/semigroup types? They look similar, but removing concat can break semigroup.
Semigroup and Monoid are similar to Alt and Plus. Semigroup is a superclass of Monoid (see here and diagram here). IOW, a type must first implement Semigroup before it can be a Monoid. So, if concat is removed, then it's no longer a Semigroup, and thus, by definition, no longer a Monoid.
The two hierarchies are similar, but conceptually, it can be helpful sometimes to think of Semigroup/Monoid as representing merging or combining, and Alt and Plus as representing choice or alternatives.
You use merge
word here. So can be semigroup considered, not only like appending
in the end, but combining values in some way?
@TrySound English is imprecise, so in reality, the only things that matter are the type signature and mathematical laws by which concat must abide:
concat :: forall s. (Semigroup s) => s -> s -> s
You can substitute Stream a
for s
and get:
concat :: forall a. Stream a -> Stream a -> Stream a
That is, given 2 streams of a
s, produce another stream of a
s. And there is an associativity law:
concat(s1, concat(s2, s3)) ~= concat(concat(s1, s2), s3)
Appending and time-based interleaving are both valid implementations. It is possible to have a Semigroup that combines values, but only in the case where you know the values (i.e. the a
s are also a Semigroup). For example, it's typical for a Maybe structure, such as purescript's Maybe, where Maybe a
is a Semigroup only if a
is a Semigroup.
An analogy for an event stream would be a merge operation that requires a
to be a Semigroup and concat
's the a
s when events are simultaneous in the two streams being merged.
Currently, most.js's concat
appends one stream to another. This was a bad decision for a data structure in which times are just as important as values, because appending causes time shifting. It's likely in most.js 2.0, we'll switch the fantasy-land Semigroup to use merge() instead, which preserves times.
Added separate test file for Alt and Plus (do you want to split them?).
Covered continueWith
, alt
, empty
, zero
with type tests. Do we need any parameter in continueWith
? Is it kind of legacy?
Is it ok to use es features in tests?
Nothing is deprecated in this PR. Just aliases for algebraic types like just/of
.
My bad, @TrySound. The deprecate comments were already there. However, I’ll then alert @briancavalier on this practice.
Plus::zero is implemented via
empty
,never
methods Not sure aboutscan
, butreduce
looks like Foldable::reduceWe can add some examples with laws around these types.