-
Notifications
You must be signed in to change notification settings - Fork 207
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
Streams became covariant #521
base: master
Are you sure you want to change the base?
Conversation
Hey @bigslycat. Thanks for the PR. Can you help me understand the real world use cases where someone might want to do this? Are there any cases for it outside of startWith? |
declare var string$: Stream<string>;
declare var number$: Stream<number>;
const stringNumber1$ = (string$: Stream<number | string>).startWith(0);
const stringNumber2$ = (string$: Stream<number | string>).merge(number$);
const stringNumber3$ = merge(string$, number$);
const stringNumber4$ = (string$: Stream<number | string>).concat(number$);
const stringNumber5$ = (string$: Stream<number | string>).recoverWith(() => of(0));
const stringNumber6$ = (string$: Stream<number | string>).continueWith(() => of(0)); Invariance makes streams not usable. Types of read-only entities can be covariant. |
@bigslycat What we're looking for is real world use cases. I want to understand the benefits to developers for allowing covariance at the highest level like this. There are benefits to invariance, and precedent. For example, lists in many statically typed languages are invariant. Since startWith is similar to list Cons, there is precedent for invariance. I'm not saying I'm against this. Rather, I just want to understand the tradeoffs and real world use cases of allowing covariance so broadly. For example, Perhaps similarly, it would be better to identify particular operations where this may be useful, and explicitly allow them to return unions? Would like to hear thoughts on that from @TylorS, @davidchase, and @Frikki as well. If we do this, I think it makes sense to do it over in most/core, which is the future of mostjs. |
Not very familiar with T$, so I have more of a commenty-question then any real feed back or anything. Does this concept of covariant as a pre-order (I am assuming it is a pre-order, but it probably is a partial order) on some hierarchy, apply for what you want? To me it sounds like you really just want just a union of javascript types (I am not talking a tagged Union Type like Either or Maybe or something, but a union as from Set Theory, basically a Type that is Please pardon my ignorance in the subject, it just seems like a type union (if you can do that with T$ that is) could be treated as the type EDIT: But this will take the Additional EDIT: |
Hey @evilsoft, good to hear from you.
This matches my current thinking. We did it for As an example using
It seems like partial order (it's antisymmetric?), but you've gone beyond my current type theory knowledge :). I'd love to know the answer. |
So from what I gather is from this article the
and of course the Dual (or Opposite or just Op) being: So while the other two are obvious Partial orders, when I draw it out bivariance, it seems to be a pre-order, where So from an order perspective they are all essentially the same things as there is an isomorphisim between all members. So they can all be the same thing as far as the object is 🌽cerned. But I may be super wrong about that, I am a JS fellow and not really a set/category theorist. But either way. I do not know if this should be at the heart, it seems the consumer of the EDIT: I drew up a couple diagrams and yes it commutes if and only if the methods on the subtypes do not override to return/accept different types. Again though, while it does commute, it commutes with constraints and cannot be generalized. I still believe that this order should not be imposed on the underlying type but should be managed by the consumer of Just my opinion and thoughts though. Please take with a grain of salt. |
With apologies to @bigslycat for coopting this thread with slightly tangential discussion :) Ah, yes, thanks. I was definitely making it too complicated in my head. It's just subtyping, which, as far as I know in Flow (and most other places) is a preorder. I'm far less sure about that when things get into weird Flow/TS territory like width subtyping, function subtypes, etc.
Hmm, possibly. It does make me a bit nervous, but is Or does it mean a Functor in this world can be: |
@bigslycat Please let us know your thoughts about going with union types as per the similar approach in @most/core merge. Thanks! |
Ping @bigslycat |
@briancavalier pong. Sorry for delay. I will answer today. |
/* @flow */
type Cat = 'cat';
type Dog = 'dog';
type Animal = Cat | Dog;
declare class Stream<+T> {}
declare var cats: Stream<Cat>;
declare var dogs: Stream<Dog>;
// We have two steams and we can to downgrade this types to supertype:
const anumals1: Stream<Animal> = cats;
const anumals2: Stream<Animal> = dogs;
// But how you can to send a Cat to anumals2?
// And how you can to send a Dog to anumals1?
// Observables are readonly. This is safe. If not, show me this in real code plz. =)
// For negative example arrays are writable and in this case you really
// can to send a Cat to anumals2 or can to send a Dog to anumals1. |
I can to show you a use case: import { of, from, Stream } from 'most';
import { mapPropsStream } from 'react-honeycombs';
declare var requestFail$: Stream<Error>;
export BlaBlaComponent: React$ComponentType<Props> = mapPropsStream(
(props$: Observable<Props>) =>
from(props$).combine(
(props, error) => ({ ...props, errror }),
(requestFail$: Stream<?Error>).startWith(null),
),
)(
...
); Look to |
Thanks for this real use case, @bigslycat. I see what you're saying: you'd like " Here are my main concerns:
It may be ok, though, since most-subject already has two type parameters: one implication is that mostjs @bigslycat I'd like to hear your thoughts on those 3 concerns. Thanks! |
@bigslycat Your most recent example seems also to work with invariant union types, as has been proposed already in this thread. It just requires Could you please respond about whether that would work for you. I realize it may not be the solution you'd prefer, but we have to consider the big picture, so it would be helpful to know if this is viable for you. If it's not, please explain and give an example of why it's not. Thanks! |
I want to downgrade a type sometimes.
Streams are read-only, so there is no need to leave them invariant.