Firstly, let me preface by saying I'm not attacking. It's an interesting
problem, I just don't understand (and I'd like to).
Cool. I'm happy to discuss it. First, forget about iterating Fixnums.
Is that not just an array?
If you know the predetermined list of items, sure it could be an array. How about we add the following constraints:
1. You can't know when to stop iterating until you fetch the last item.
2. It's prohibitive to prefetch all the items into an array first, and then traverse it.
3. The implementation to fetch the next item, and deciding where to stop is handled differently for many different configurations and needs to be passed in externally.
4. The iterator needs to do some internal setup before/after yielding each item.
> You've turned my yielder into an array, where the list of items is
> predetermined, and there's no way to control it (it's missing step 2).
Except that I'm the one that chose value n=5, and I'm also the one that
chose operations [n,n*10,n*100]. I controlled all of that. I did this:
# step 1:
num = 5
# step 2:
repeater =
repeater << num
repeater << num * 10
repeater << num * 100
# step 3:
repeater.each {...}
.. except that, in this case, I was able to do it offline, because I
already know what 5, 5*10, and 5*100 are (even though I wrote `100`.. oops).
See, there's too much assumed knowledge all in one place there. I need to separate responsibilities into 3 places to hide complexity, and each part knows its own role:
1. Getting some external object that we start with (It's not a simple Fixnum 5)
2. Defining how we need to iterate over it.
3. Abstracting that iteration as a simple "each"
Parts 1 and 2 are somewhat related; they have some knowledge & correspondence with each other but I want to extract a common interface for the iteration part so it can apply to anything that's passed in.
This is where my understanding falls down. Is there another actor in
this scenario that I'm missing? I think it would help me if you could
come up with another contrived example that shows how an array is
insufficient. Is the initial value actually a stateful creature that you
don't want to mutate until calling #each ?
Correct, it is not a single class; it is stateful; stuff happens only once yielded to each. You get 3 for 3!
Or, alternatively, explain why I should use your repeater class instead
of doing this:
class Foo
def initialize
@num = 5
end
def each
yield @num
yield @num * 10
yield @num * 100
end
end
What entices me to pass the value and the effective body of #each to
your utility class, instead of just doing it myself? What else do you add?
Your "each" is defined internally in your class. I need that logic to be passed from a DSL block from a config file. How about this for a hopefully more revealing example DSL:
repeat do |pages, remote_source|
loop do
pages << remote_source
break if remote_source.data.split("\n").size < 100
remote_source.query_params["page"] += 1
end
end
This is a single example of a data source. There are many like it, but only some use HTTP with a page query. Another one might get the number of pages from the remote source and use 1.upto(pg), or list some files from FTP... it all depends and it's impossible to hard-code the iteration logic into my data Provider or its supporting Repeater class.
Make sense?
Andrew Vit
···
On 14-03-17, 18:19, Matthew Kerwin wrote: