In message “Re: ruby-dev summary 18711-18810” on 02/11/18, “Aleksei Guzev” aleksei.guzev@bigfoot.com writes:
If the longest sequence would be chosen, what value should receive the
absent items?
nil
But this will raise TypeError in the case of incompatible type/length of the
sequences. This would hide the error and violate “…* (on zip) We should
not raise exceptions when the length of the components are differ…”
I’m not sure if I get what you mean. How does it hide errors and
violate the assumption? nil does not always raise TypeError, if you
know the value may be nil. If “zip” itself raises exceptions, there’s
no workaround.
Do you mean that map_with_index has been rejected on its own, or just
as a zip implementation?
Hmmmm… I’ll try to think of some examples. It certainly seems to
me to be at least as useful as each_with_index, potentially (i.e., if
it existed
Do you know implementations like (from Ruby CVS)
rough/lib/generator.rb? Why do you think these are inefficient?
I haven’t seen that (but I’ll definitely take a look). I think generators
can be implemented efficiently. In my experience with generators in other
languages, the time efficiency is good enough, and depending on the
underlying language implementation, they can be costly of space since in
some implementations they require their own contiguous stacks (almost like
threads – to support suspended C stacks in the underlying C code).
If Ruby has an efficient generator capability, it would be a good way to
implement “zip” without the space cost of creating an entire copy of all of
the pointers in all involved sequences.
BTW, I have quite a bit of experience with languages that support
generators, primarily with Icon whose generators are normally used at the
site of the generation (like Ruby’s iterators) but can be also be captured
in a “co-expression” to be used anywhere and any time to grab values from
the generator (like Python’s generators). Like Matz says, the Ruby iterator
mechanism is simpler and nicely handles 99% of the cases that usually come
up. On the other hand, in those relatively rare cases where when parallel
iteration requirements arise, the “use anywhere” variety of generator can
make the task much more pleasant.
Maybe Ruby could consider approaches like Icon’s where iterators are
normally internal (like they are now), but can be converted to external for
those occasional needs (apologies if this has already been discussed to
death). I attached a small experimental Ruby class I wrote a long time ago
that implements something like this. The implementation uses threads and
queues, so is not very efficient. However, an API of this sort could
possibly be implemented efficiently at a lower level. Here is the
description from the top of the code:
map_with_index was rejected just because no one proposed its typical
usage. YANGI principle applied.
Hmmmm… I’ll try to think of some examples. It certainly seems to
me to be at least as useful as each_with_index, potentially (i.e., if
it existed
I’d go so far as to say that map_with_index
does for map exactly what each_with_index
does for each.
I do find it useful.
It’s used in scanf, David, isn’t it?
Almost, but in the end we didn’t use it. I can’t remember the whole
sequence of events…
I definitely agree that map_with_index (or map_with_indices) is as
deserving of existence as each_with_index. Not that each_with_index
is itself terribly critical:
Do you mean that map_with_index has been rejected on its own, or just
as a zip implementation?
Hmmmm… I’ll try to think of some examples. It certainly seems to
me to be at least as useful as each_with_index, potentially (i.e., if
it existed
I guess, if zip existed, you could simulate that with:
a = [“a”, “b”, “c”, “d”]
a.zip(0…a.length).map {|item, index| …}
And if map_with_index existed, you could define #zip in terms of it
def zip(*others)
map_with_index {|e,i| [e,others.map{|n| n[i]}]}
end
“Yukihiro Matsumoto” matz@ruby-lang.org wrote in message
news:1037661991.163626.9880.nullmailer@picachu.netlab.jp…
Hi,
If the longest sequence would be chosen, what value should receive the
absent items?
nil
But this will raise TypeError in the case of incompatible type/length of
the
sequences. This would hide the error and violate “…* (on zip) We
should
not raise exceptions when the length of the components are differ…”
I’m not sure if I get what you mean. How does it hide errors and
violate the assumption? nil does not always raise TypeError, if you
know the value may be nil. If “zip” itself raises exceptions, there’s
no workaround.
matz.
Hiding error means one would receive diagnostic message of TypeError while
the error is not of that type. It’s an index error. This will make locating
bugs harder.
Violation means that one calling “zip” will get an exception regardless of
what method has raised it. It doesn’t matter who raised an exception.
Matters its nature and its origin in the user code(!), not library one.
···
In message “Re: ruby-dev summary 18711-18810” > on 02/11/18, “Aleksei Guzev” aleksei.guzev@bigfoot.com writes:
Do you know implementations like (from Ruby CVS)
rough/lib/generator.rb? Why do you think these are inefficient?
I haven’t seen that (but I’ll definitely take a look). I think
generators can be implemented efficiently. In my experience with
generators in other languages, the time efficiency is good enough, and
depending on the underlying language implementation, they can be
costly of space since in some implementations they require their own
contiguous stacks (almost like threads – to support suspended C
stacks in the underlying C code).
Hi Bob,
thanks for the clarification. I wasn’t sure what you meant by
“inefficient”. The (generic) iterator implementations presented so
far seem to be at least space-inefficient, because they either create
arrays or use threads/continuations.
If Ruby has an efficient generator capability, it would be a good way
to implement “zip” without the space cost of creating an entire copy
of all of the pointers in all involved sequences.
Not yet, but Matz seems to be working on it.
BTW, I have quite a bit of experience with languages that support
generators (…)
I attached a small experimental Ruby class I
wrote a long time ago that implements something like this. The
implementation uses threads and queues, so is not very efficient.
But the code is a nice read, thanks a lot.
However, an API of this sort could possibly be implemented efficiently
at a lower level.
If I understood Matz correctly he’s working on a low-level
implementation of zip, but doesn’t like external iterators (sigh).
Tuesday, November 19, 2002, 8:59:03 PM, you wrote:
that implements something like this. The implementation uses
threads and queues, so is not very efficient. However, an API of
this sort could possibly be implemented efficiently at a lower
level. Here is the description from the top of the code:
ruby iterators insert iterator body in CALL STACK between caller code
and block evaluated by iterator - so that you hold a state of iterator
body but no state for iterated block
caller
→ iterator
→ block
“.next-style” iterators use opposite scheme:
outside code
→ loop body
→ iterator body
if you want to hold BOTH state of iterator (local vars, ability to
do yield at different places) and state of loop (local vars, ability
to do .next at different places) you need coroutines
Hiding error means one would receive diagnostic message of TypeError while
the error is not of that type. It’s an index error. This will make locating
bugs harder.
But it happens all the time in dynamic languages. Why this bothers
you so much?
Violation means that one calling “zip” will get an exception regardless of
what method has raised it. It doesn’t matter who raised an exception.
Matters its nature and its origin in the user code(!), not library one.
You can always get the error position in the user code, either in
stack trace in the error message, or in caller information.
matz.
···
In message “Re: ruby-dev summary 18711-18810” on 02/11/19, “Aleksei Guzev” aleksei.guzev@bigfoot.com writes:
“Yukihiro Matsumoto” matz@ruby-lang.org wrote in message
news:1037682098.970812.11088.nullmailer@picachu.netlab.jp…
Hi,
Hiding error means one would receive diagnostic message of TypeError
while
the error is not of that type. It’s an index error. This will make
locating
bugs harder.
But it happens all the time in dynamic languages. Why this bothers
you so much?
All the time people die in auto crashes. But noone says ‘Let them die…’
If there exists an opportunity to work around this issue, why not to do
this?
Of course, I understand there could be some problems implemeting raising
exceptions.
Violation means that one calling “zip” will get an exception regardless
of
···
In message “Re: ruby-dev summary 18711-18810” > on 02/11/19, “Aleksei Guzev” aleksei.guzev@bigfoot.com writes:
what method has raised it. It doesn’t matter who raised an exception.
Matters its nature and its origin in the user code(!), not library one.
You can always get the error position in the user code, either in
stack trace in the error message, or in caller information.
And if map_with_index existed, you could define #zip in terms of it
def zip(*others)
map_with_index {|e,i| [e,others.map{|n| n[i]}]}
end
(or something)
That would only work for objects with #. But then you
already said it’s ok to you…
Yes, although I’d be interested in hearing people’s ideas about cases
where that wasn’t sufficient. I guess maybe objects which generate a
random sequence of some kind?