Heyho, I've been playing around with implementing reactivex (or rather 
expanding an existing lib to more fully implement reactivex), which is a lib to 
implement reactive programming, aka observables, subjects etc. . This allows 
you to make values depend on one another via a push instead of a pull 
mechanism. There I stumbled into a problem.

Strictly speaking, the concepts of reactive x create a graph of 3 types of 
nodes:

  * "Source" nodes that are the start for a value that gets pushed through the 
graph/chain of nodes. These are Subjects or Observables
  * "Intermediate/Operator" nodes which get attached to "Source" nodes. They 
receive values from the SourceNode and transform it in some case (think the 
`map` operator on lists in functional program) or introduce conditions on it 
(think the `filter` or `tap` operators on lists in functional programming).
  * "Consumer/Subscriber" nodes which can subscribe to any of the other 2 nodes 
and receive their value and do some final operation with it.



So more conceptually speaking I want to implement a graph that will not contain 
cycles, but can strictly speaking in totality have an arbitrary amount of 
generic types.

That naturally runs into a problem with the type system, because the 
implementation I can think of would roughly look like this:
    
    
    type Subject[T] = ref object # Source node
      observer: Observer[T]
    
    type Observable[T] = ref object # Source node
      observer: Observer[T]
      value: T
    
    type ParentKind = enum
      pkObservable, pkSubject, pkOperator
    
    type Operator[T, S] = ref object # Intermediate/Operator node
      case kind: ParentKind:
      of pkObservable:
        obsParent: Observable[T]
      of pkSubject:
        subjParent: Subject[T]
      of pkOperator:
        opParent: Operator[R, T] # <-- Wait, so `Operator` needs the third 
parameter R, but now this might be Q, R, T, S if your parent operator now has 
Q, R, T... etc.
    
    
    Run

The problem is the second you want to chain nodes (aka your parent is another 
Operator-Node) then you also need the types of the parent. So suddenly instead 
of generic types `T` and `S`, you also need `R`. But then you also may have a 
parent-node with that has a parent, node. In _that_ scenario your OperatorNode 
type suddenly needs also `Q`, and so on and so forth.

Is there some kind of way to solve this in a fashion that doesn't create 
limitations?

If RXJS is anything to go by, then this seems like it might be impossible to 
solve in an unlimited fashion and I should just define a dozen or so `Operator` 
types with various amounts of generic types. Because _they_ apparently gave up 
looking at their type-declarations for their `pipe` function
    
    
      pipe(): Observable<T>;
      pipe<A>(op1: OperatorFunction<T, A>): Observable<A>;
      pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): 
Observable<B>;
      pipe<A, B, C>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, 
op3: OperatorFunction<B, C>): Observable<C>;
      pipe<A, B, C, D>(
        op1: OperatorFunction<T, A>,
        op2: OperatorFunction<A, B>,
        op3: OperatorFunction<B, C>,
        op4: OperatorFunction<C, D>
      ): Observable<D>;
      pipe<A, B, C, D, E>(
        op1: OperatorFunction<T, A>,
        op2: OperatorFunction<A, B>,
        op3: OperatorFunction<B, C>,
        op4: OperatorFunction<C, D>,
        op5: OperatorFunction<D, E>
      ): Observable<E>;
    .... they play this game up to I
    
    
    Run

Reply via email to