- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
bridging Iterator and Traversable
Sun, 2009-08-16, 18:54
I am finding myself frustrated trying to improve public-facing APIs due
to the iterator/iterable issue. One ends up having to take only one or
the other based on some arbitrary reasoning, or write the method twice
even though the body is identical. And because I am dealing with
methods which require default implicit arguments, and methods with
default arguments can't be overloaded, I actually have to come up with
two different names.
Rather than perpetuate this any further, I tried to dream up the
smallest change which would make it go away. Obviously we don't want to
blindly convert Iterators to Traversables, but we could do this:
class OnceTraversable[+A](self: Traversable[A]) extends Traversable[A] {
def foreach[U](f: A => U): Unit = self foreach f
}
object Iterator {
implicit def onceTraversableWrapper[A](self: Iterator[A]): OnceTraversable[A] =
new OnceTraversable[A](self.toStream)
}
object Traversable {
implicit def onceTraversableWrapper[A](self: Traversable[A]): OnceTraversable[A] =
new OnceTraversable[A](self)
}
def writeMethodOnce[T](xs: OnceTraversable[T]) = xs foreach println
This mostly works as far as I can see. How do we feel about it? Feel
free to come up with a better name.
I should note that before I did this I tried having Iterator extend
Traversable, on the theory that Traversable's contract need not include
repeatability since nobody's really using it yet. Based on the
screenfuls of unhappiness, that looked like a much more involved road so
I abandoned it, but it might be doable if it was determined that is the
Right Way.
Here is the issue implied by "mostly", which is a regression since 2.7.
It looks like implicit resolution is now attempted before the type
inference is complete.
// a.scala
class Bob[T]
implicit def foo2bar[T](xs: List[T]): Bob[T] = new Bob[T]
var x: Bob[Int] = null
x = List(1,2,3)
% scala27 -i a.scala
[snip]
x: Bob[Int] = Bob@af5951
% scala28 -i a.scala
[snip]
:7: error: type mismatch;
found : List[Any]
required: Bob[Int]
x = List(1,2,3)
^
x = List[Int](1,2,3) does work in 2.8.
Sun, 2009-08-16, 20:17
#2
Re: bridging Iterator and Traversable
On Sun, Aug 16, 2009 at 08:47:41PM +0200, martin odersky wrote:
> For method arguments it's most convenient to provide for both both
> versions, as is done in Traversabloe.++ for example.
Well, like I said I can't reuse the method name because you can't
overload if you have default arguments. I would suppose ++ would be a
lot less appealing if the methods were called iter_++ and trav_++.
Sun, 2009-08-16, 20:37
#3
Re: bridging Iterator and Traversable
On Sun, Aug 16, 2009 at 9:14 PM, Paul Phillips wrote:
> On Sun, Aug 16, 2009 at 08:47:41PM +0200, martin odersky wrote:
>> For method arguments it's most convenient to provide for both both
>> versions, as is done in Traversabloe.++ for example.
>
> Well, like I said I can't reuse the method name because you can't
> overload if you have default arguments. I would suppose ++ would be a
> lot less appealing if the methods were called iter_++ and trav_++.
>
In that case, I'd either not use default arguments, or standardize on
Traversable.
I believe the distinction between Iterable and Iterator is
fundamental, so I do not want to hide that distinction with implicits.
Cheers
Sun, 2009-08-16, 20:57
#4
Re: bridging Iterator and Traversable
On Sun, Aug 16, 2009 at 09:35:03PM +0200, martin odersky wrote:
> In that case, I'd either not use default arguments, or standardize on
> Traversable. I believe the distinction between Iterable and Iterator
> is fundamental, so I do not want to hide that distinction with
> implicits.
I am fine with avoiding default arguments in many cases (although I
think having to choose here would be an ongoing rock in the shoe) but
default implicit arguments are the linchpin not only of my attempt to
make encodings work sensibly without lots of API noise, but a whole
world of other improvements.
So I'll standardize on Traversable and people can .toStream or whatever.
Speaking of iterators, I'm just working with what was already there.
scala.io.Source extends Iterator[Char] so I was taking iteratorhood as a
given. In almost every case Source could actually be (and would be far
superior as) an Iterable.
In fact the distinction between iterators and iterables has essentially
been hand-coded into Source, in that you supply a reset() method as an
argument, which implies it can be traversed multiple times, which
implies you didn't really want an iterator.
So in Source right now rather than hiding the distinction with
implicits, we're hiding it by failing at runtime if you try to call
reset() on a non-resettable Source. Given the division in the type
system I'm not sure how I would improve this given the chance, but it
does seem likely improvement is available.
Sun, 2009-08-16, 22:17
#5
Re: bridging Iterator and Traversable
Hi Paul,
Please don't take scala.io.source as a standard to follow! Even his
author said that it was a hastily written stopgap thing, just to make
some xml stuff work. It's a bit sad that that's all there is wrt Scala
io. In any case, much better to rewrite io.source than to follow it.
Cheers
I am not convinced this is progress. At least implicit in the contract
of a Traversable is that you can traverse it multiple times.
The best ryule for a public interface is to return a traversable when
you can. You can always turn it into am iterator if you need to. For
method arguments it's most convenient to provide for both both
versions, as is done in Traversabloe.++ for example.
Cheers