snivilised / lorax

🌟 reactive extensions for go (a pseudo rxgo version 3)
MIT License
4 stars 0 forks source link

support for interfaces #259

Closed plastikfan closed 4 months ago

plastikfan commented 4 months ago

in particularly when using RangeIteratorByProxy and Envelope. Oops, I think I made a mistake here. There is no real need for support for interfaces and this is the reason why:

When using the range operator, we are saying that we want to create an observable from automatically generated items. We delegate to range the capability to generate events, rather than rely on them being created explicitly via from an event source (eg the traverse navigator).

From the perspective of traverse, there is never any need for events to be created on our behalf as the navigator is the only source of events.

When you use the range operator, the iterator creates concrete types from an index of that concrete type. You don't say, create me an instance of an interface, because actually, what does that mean? You are essentially saying, create me an concreate instance of this interface, at which point, what is the point of the interface definition.

If you try to define a channel of Item[I], where item I is an interface, then contrary to what you might think, you can't send an Item[O] down a channel of Item[I] even when O satisfies interface I, because the type Item gets in the way.

When using rx, it really only makes sense to define them based upon concrete types, not interfaces.

plastikfan commented 4 months ago

some code ...

            Context("using interface", func() {
                When("positive count", func() {
                    It("🧪 should: create observable", func() {
                        // Test_Range
                        defer leaktest.Check(GinkgoT())()

                        obs := rx.RangePF(&rx.RangeIteratorIfProxy[rx.Interface[wrapper, int], int]{
                            StartAt: rx.Interface[wrapper, int]{P: 5},
                            By:      rx.Interface[wrapper, int]{P: 1},
                            Whilst:  rx.MoreThanPF(rx.Interface[wrapper, int]{P: 8}),
                        })

                        rx.Assert(context.Background(), obs,
                            rx.HasItems[rx.Interface[wrapper, int]]{
                                // Expected: []widget{
                                // rx.Interface[wrapper, int]{id: 5},

                                /*
                                    cannot use rx.Interface[wrapper, int]{…}
                                    (value of type rx.Interface[wrapper, int])
                                    as []rx.Interface[wrapper, int] value in struct literal
                                */

                                // rx.Interface[wrapper, int]{id: 5},
                                // {id: 5}
                                //  {id: 6},
                                //  {id: 7},
                                // },
                            },
                        )

                    })
                })
            })
// RangeIteratorIfProxy iterator required for struct types of T, where the
// client has nominated a member of T to be the proxy field with
// which numeric operations are performed to generate indexes for iteration.
type RangeIteratorIfProxy[T ProxyField[T, O], O Numeric] struct {
    StartAt T
    By      T
    Whilst  WhilstFunc[T]
    zero    T
}

// Init performs pre iteration check and returns an error on failure.
func (i *RangeIteratorIfProxy[T, O]) Init() error {
    if i.Whilst == nil {
        return RangeMissingWhilstError
    }

    return nil
}

// Start should return the initial index value
func (i *RangeIteratorIfProxy[T, O]) Start() (*T, error) {
    if i.By.Field() == 0 {
        return &i.zero, BadRangeIteratorError{
            // we can't modify By because By is not guaranteed to be Numeric
            //
            error: "iterator step by not defined",
        }
    }

    if i.Whilst == nil {
        return &i.zero, BadRangeIteratorError{
            error: "iterator missing while",
        }
    }

    return &i.StartAt, nil
}

// Step is used by Increment and defines the size of increment for each iteration
func (i *RangeIteratorIfProxy[T, O]) Step() O {
    return i.By.Field()
}

// Increment returns a pointer to a new instance of with incremented index value
func (i *RangeIteratorIfProxy[T, O]) Increment(index *T) *T {
    (*index).Inc(index, i.By)

    return index
}

// While defines a condition that must be true for the loop to
// continue iterating.
func (i *RangeIteratorIfProxy[T, O]) While(current T) bool {
    return i.Whilst(current)
}
type (
    // Interface
    Interface[T any, O Numeric] struct {
        zero T
        P    O
    }
)

func (in Interface[T, O]) Field() O {
    return in.P
}

func (in Interface[T, O]) Inc(index *Interface[T, O], by Interface[T, O]) *Interface[T, O] {
    index.P += by.P

    return index
}

func (in Interface[T, O]) Index(i int) *Interface[T, O] {
    return &Interface[T, O]{
        P: O(i),
    }
}
// RangeIf creates an Observable that emits count sequential integers beginning
// at start, for non numeric types, which do contain a nominated proxy Numeric member
func RangeIf[T ProxyField[T, O], O Numeric](iterator RangeIteratorPF[T, O],
    opts ...Option[T],
) Observable[T] {
    if err := iterator.Init(); err != nil {
        return Thrown[T](err)
    }

    return &ObservableImpl[T]{
        iterable: newRangeIterablePF(iterator, opts...),
    }
}