diff --git a/README.md b/README.md index b8acdbc..2ff6abc 100644 --- a/README.md +++ b/README.md @@ -168,10 +168,10 @@ The following is an example of how to use the Range operator for scalar types: ___NumericRangeIterator___ is defined for all numeric types and is therefore able to use the native operators for calculation operations. -For struct types, the above is identical, but instead using ___ProxyRangeIterator___: +For struct types, the above is identical, but instead using ___RangeIteratorByProxy___: ```go - obs := rx.RangePF(&rx.ProxyRangeIterator[widget, int]{ + obs := rx.RangePF(&rx.RangeIteratorByProxy[widget, int]{ StartAt: widget{id: 5}, By: widget{id: 1}, Whilst: rx.LessThanPF(widget{id: 8}), @@ -230,12 +230,32 @@ func (w widget) Inc(index *widget, by widget) *widget { This may look strange, but is necessary since the type ___T___ can not be defined with pointer receivers with respect to the ___Numeric___ constraint. The reason for this is to keep in line with the original rxgo functionality of being able to compose an observable with literal scalar values and we can't take the address of literal scalars that would be required in order to be able to define ___Inc___ as: -> func (w *widget) Inc(by widget) *widget +```go +func (w *widget) Inc(by widget) *widget +``` So ___Numeric___ receivers on ___T___ being of the non pointer variety is a strict invariant. The aspect to focus on in ___widget.Inc___ is that ___index___ is incremented with the ___by___ value, not ___w.id___. Effectively, widget is passed a pointer to its original self as index, but w is the copy of index in which we're are running. For this to work properly, the original widget (index) must be incremented, not the copy (w), which would have no effect, resulting in an infinite loop owing to the exit condition never being met. +#### ๐Ÿ“จ Envelope + +The above description regarding pointer receivers on T may appear to be burdensome for prospective types. However, there is a mitigation for this in the form of the type ___Envelope[T any, O Numeric]___. This serves 2 purposes: + ++ __permit pointer receiver:__ The envelope wraps the type T addressed as a pointer and also contains a __numeric__ member P of type O. This is particularly useful large struct instances, where copying by value could be non trivial and thus inefficient. + ++ __satisfy ProxyField constraint:__ The presence of the proxy field P means that Envelope is able to implement all the methods on the ___ProxyField___ constraint, freeing the client from this obligation. + +The following is an example of how to use the ___Envelope___ with the iterator ___RangeIteratorByProxy___: + +```go + obs := rx.RangePF(&rx.RangeIteratorByProxy[rx.Envelope[nugget, int], int]{ + StartAt: rx.Envelope[nugget, int]{P: 5}, + By: rx.Envelope[nugget, int]{P: 1}, + Whilst: rx.LessThanPF(rx.Envelope[nugget, int]{P: 8}), + }) +``` + ### ๐ŸŽญ Map The ___Map___ functionality poses a new challenge. If we wanted to map a value of type ___T___ to a value other than ___T___, the mapped to value could not be sent through the channel because it is of the wrong type. A work-around would be to use an opaque instance of Item, but then that could easily become very messy as we no longer have consistent types of emitted values which would be difficult to keep track of. diff --git a/rx/factory_test.go b/rx/factory_test.go index 4048df2..acbf8d6 100644 --- a/rx/factory_test.go +++ b/rx/factory_test.go @@ -1037,13 +1037,13 @@ var _ = Describe("Factory", func() { }) }) - Context("ProxyRangeIterator", func() { + Context("RangeIteratorByProxy", func() { When("positive count", func() { It("๐Ÿงช should: create observable", func() { // Test_Range defer leaktest.Check(GinkgoT())() - obs := rx.RangePF(&rx.ProxyRangeIterator[widget, int]{ + obs := rx.RangePF(&rx.RangeIteratorByProxy[widget, int]{ StartAt: widget{id: 5}, By: widget{id: 1}, Whilst: rx.LessThanPF(widget{id: 8}), @@ -1069,7 +1069,7 @@ var _ = Describe("Factory", func() { // Test_Range defer leaktest.Check(GinkgoT())() - obs := rx.RangePF(&rx.ProxyRangeIterator[rx.Envelope[nugget, int], int]{ + obs := rx.RangePF(&rx.RangeIteratorByProxy[rx.Envelope[nugget, int], int]{ StartAt: rx.Envelope[nugget, int]{P: 5}, By: rx.Envelope[nugget, int]{P: 1}, Whilst: rx.LessThanPF(rx.Envelope[nugget, int]{P: 8}), diff --git a/rx/iterable-range.go b/rx/iterable-range.go index f0403e0..e1ea56d 100644 --- a/rx/iterable-range.go +++ b/rx/iterable-range.go @@ -152,10 +152,10 @@ func (i *NumericRangeIterator[T]) While(current T) bool { return i.Whilst(current) } -// ProxyRangeIterator iterator required for struct types of T, where the +// RangeIteratorByProxy 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 ProxyRangeIterator[T ProxyField[T, O], O Numeric] struct { +type RangeIteratorByProxy[T ProxyField[T, O], O Numeric] struct { StartAt T By T Whilst WhilstFunc[T] @@ -164,7 +164,7 @@ type ProxyRangeIterator[T ProxyField[T, O], O Numeric] struct { // Init is invoked prior to iteration and returns an error if not // defined correctly. -func (i *ProxyRangeIterator[T, O]) Init() error { +func (i *RangeIteratorByProxy[T, O]) Init() error { if i.Whilst == nil { return RangeMissingWhilstError } @@ -174,7 +174,7 @@ func (i *ProxyRangeIterator[T, O]) Init() error { // Start should return the initial index value. If By has // not been set, a panic occurs -func (i *ProxyRangeIterator[T, O]) Start() (*T, error) { +func (i *RangeIteratorByProxy[T, O]) Start() (*T, error) { if i.By.Field() == 0 { panic("bad by value, can't be zero") } @@ -188,12 +188,12 @@ func (i *ProxyRangeIterator[T, O]) Start() (*T, error) { return &index, nil } -func (i *ProxyRangeIterator[T, O]) Step() O { +func (i *RangeIteratorByProxy[T, O]) Step() O { return i.By.Field() } // Increment increments index value -func (i *ProxyRangeIterator[T, O]) Increment(index *T) *T { +func (i *RangeIteratorByProxy[T, O]) Increment(index *T) *T { // This does look a bit strange but its a work around // for the fact that the instance of T is implemented with // non-pointer receivers and therefore can't make modifications @@ -212,7 +212,7 @@ func (i *ProxyRangeIterator[T, O]) Increment(index *T) *T { // While defines a condition that must be true for the loop to // continue iterating. -func (i *ProxyRangeIterator[T, O]) While(current T) bool { +func (i *RangeIteratorByProxy[T, O]) While(current T) bool { return i.Whilst(current) }