- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Re: map2/map3
Tue, 2009-05-19, 15:57
I kept the reply at the bottom to myself for a day or so (I feel
confident it was intended for the list) but I give up! Anyone who can
make map, flatMap etc. work on Tuple2 in the manner implied is going to
get a big freaking gold star from me. At least it gave me a good reason
to read the collections paper a few times.
The wrapper in its commented out form looks like this:
implicit def tupleOfIterableWrapper
[CC[+B] <: Iterable[B] with IterableTemplate[CC, B], A1, A2]
(tuple: (CC[A1], Iterable[A2])) =
new IterableOps[CC, A1, A2](tuple)
(That's a bit dated because CC requires a type parameter, but anyway)
the gist here is that with some combination of type parameters, higher
kinded types, implicit builders, and fairy people, we can transform a
tuple of two Iterables to an Iterable of tuples:
((I1 <: Iterable[T1], I2 <: Iterable[T2])) => Iterable[(T1,T2)]
And that's the wimpy solution, ideally the shape of the result would
mirror the shape of collection I1.
I find that no matter what I do, the element type parameters will not be
inferred correctly, so I have to specify them to the wrapper. This of
course defeats the "implicit" part.
It's not even clear to me this is possible because of erasure - I tried
throwing Manifests at it, accomplished nothing.
Yesterday's reply follows:
On Mon, May 18, 2009 at 11:50:07AM +0200, martin odersky wrote:
> There's a bunch of code in Tuple2 that's currently still commented out
Oh oops, I had looked at Tuple3 and not seen anything but not Tuple2.
> and that's supposed to implement this. It also needs to be generalized
> for higher arities. I don't have the time to do it right now, so if
> somebody wants to have a go, please do!
>
> (The idea is that tuple classes up to some modest arity (3?, 4?, 5?)
> would support operations map, flatMap, foreach, forall, exists, zip.)
Tue, 2009-05-19, 17:17
#2
Re: map2/map3
Using the following definitions:
class IterableOps
[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2]
(tuple: (CC[A1], Iterable[A2]))
implicit def tupleOfIterableWrapper
[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2]
(tuple: (CC[A1], Iterable[A2]))
= new IterableOps[CC, A1, A2](tuple)
I get the following:
scala> val t = (List(1, 2, 3), List(6, 5, 4))
t: (List[Int], List[Int]) = (List(1, 2, 3), List(6, 5, 4))
scala> tupleOfIterableWrapper[List, Int, Int](t)
res3: IterableOps[List, Int, Int] = IterableOps@2365fe
scala> tupleOfIterableWrapper(t)
<console>:17: error: inferred kinds of the type arguments (List[Int],A1,Int) do not conform to the expected kinds of the type parameters (type CC,type A1,type A2).
List[Int]'s type parameters do not match type CC's expected parameters: class List has one type parameter, but type CC has one
tupleOfIterableWrapper(t)
^
<console>:17: error: type mismatch;
found : (List[Int], List[Int])
required: (List[A1], Iterable[Int])
tupleOfIterableWrapper(t)
^
The error message of "class List has one type parameter, but type CC has one" certainly seems suspicious there...
- Colin
class IterableOps
[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2]
(tuple: (CC[A1], Iterable[A2]))
implicit def tupleOfIterableWrapper
[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2]
(tuple: (CC[A1], Iterable[A2]))
= new IterableOps[CC, A1, A2](tuple)
I get the following:
scala> val t = (List(1, 2, 3), List(6, 5, 4))
t: (List[Int], List[Int]) = (List(1, 2, 3), List(6, 5, 4))
scala> tupleOfIterableWrapper[List, Int, Int](t)
res3: IterableOps[List, Int, Int] = IterableOps@2365fe
scala> tupleOfIterableWrapper(t)
<console>:17: error: inferred kinds of the type arguments (List[Int],A1,Int) do not conform to the expected kinds of the type parameters (type CC,type A1,type A2).
List[Int]'s type parameters do not match type CC's expected parameters: class List has one type parameter, but type CC has one
tupleOfIterableWrapper(t)
^
<console>:17: error: type mismatch;
found : (List[Int], List[Int])
required: (List[A1], Iterable[Int])
tupleOfIterableWrapper(t)
^
The error message of "class List has one type parameter, but type CC has one" certainly seems suspicious there...
- Colin
Tue, 2009-05-19, 17:27
#3
Re: map2/map3
On Tue, May 19, 2009 at 11:10:44AM -0500, Colin Bullock wrote:
> :17: error: type mismatch;
> found : (List[Int], List[Int])
> required: (List[A1], Iterable[Int])
> tupleOfIterableWrapper(t)
> ^
Indeed, this is the error I faced off against about fifty times
yesterday (one can choose between a required "List[A1]" and a required
"List[Nothing]" by fiddling with variance.)
> The error message of "class List has *one* type parameter, but type CC
> has * one*" certainly seems suspicious there...
I figured it was the new math.
Tue, 2009-05-19, 18:57
#4
Re: map2/map3
On Tue, May 19, 2009 at 6:22 PM, Paul Phillips wrote:
> On Tue, May 19, 2009 at 11:10:44AM -0500, Colin Bullock wrote:
>> :17: error: type mismatch;
>> found : (List[Int], List[Int])
>> required: (List[A1], Iterable[Int])
>> tupleOfIterableWrapper(t)
>> ^
>
> Indeed, this is the error I faced off against about fifty times
> yesterday (one can choose between a required "List[A1]" and a required
> "List[Nothing]" by fiddling with variance.)
>
>> The error message of "class List has *one* type parameter, but type CC
>> has * one*" certainly seems suspicious there...
>
> I figured it was the new math.
>
I think that's one of the known problems with inference for hk types.
But I am thinkingof some possible improvements there thta I'd try out
first, before coming back to the Tuple2.map issue.
Cheers
Tue, 2009-05-19, 21:07
#5
Re: map2/map3
On Tue, May 19, 2009 at 07:50:56PM +0200, martin odersky wrote:
> I think that's one of the known problems with inference for hk types.
> But I am thinkingof some possible improvements there thta I'd try out
> first, before coming back to the Tuple2.map issue.
I notice a little tardily that the existing implicits along these lines
seem to have all the same issues going.
scala> import collection.Traversable._
import collection.Traversable._
scala> List((1,2),(3,4),(5,6)) unzip
:8: error: value unzip is not a member of List[(Int, Int)]
List((1,2),(3,4),(5,6)) unzip
^
scala> pairTraversableWrapper(List((1,2),(3,4),(5,6)))
:8: error: inferred type arguments [List[(Int, Int)],A1,A2] do not conform to method pairTraversableWrapper's type parameter bounds [This <: Traversable[(A1, A2)],A1,A2]
pairTraversableWrapper(List((1,2),(3,4),(5,6)))
^
scala> pairTraversableWrapper[List[(Int,Int)],Int,Int](List((1,2),(3,4),(5,6)))
res3: collection.Traversable.PairTraversableOps[List[(Int, Int)],Int,Int] = scala.collection.Traversable$PairTraversableOps@c75ad4
scala> import collection.immutable.List._
import collection.immutable.List._
scala> res3 unzip
:12: error: no implicit argument matching parameter type scala.collection.generic.BuilderFactory[Int,That2,List[(Int, Int)]] was found.
res3 unzip
^
scala> res3.unzip[List[Int], List[Int]]
res5: (List[Int], List[Int]) = (List(1, 3, 5),List(2, 4, 6))
Wed, 2009-05-27, 13:37
#6
Re: map2/map3
I think that's one of the known problems with inference for hk types.I'm currently working on implementing type constructor inference -- still a little jetlagged, but I hope to have a proof of concept very soon.
cheersadriaan
Wed, 2009-06-03, 09:37
#7
Re: map2/map3
Hi,
My first prototype for tcpoly inference is available at http://github.com/adriaanm/scala/tree/tcpolyinfer
More test cases (or bug reports/fixes ;-)) greatly appreciated!
Behold:
Welcome to Scala version 2.8.0.r0-b20090602175319 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_07). Type in expressions to have them evaluated. Type :help for more information.
scala> def test[CC[+X] <: Iterable[X], A](xs: CC[A]): CC[A] = xs test: [CC[+X] <: Iterable[X],A](xs: CC[A])CC[A]
scala> val xs = test(List(1,2)) // List type constructor omitted xs: List[Int] = List(1, 2) // TADA
scala> val xs2: List[Int] = test(List(1,2)) xs2: List[Int] = List(1, 2)
scala> import scala.collection.generic._ import scala.collection.generic._
scala>
scala> class IterableOps[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2])) defined class IterableOps
scala> def tupleOfIterableWrapper[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2])) = new IterableOps[CC, A1, A2](tuple) tupleOfIterableWrapper: [CC[+B] <: Iterable[B] with scala.collection.generic.IterableTemplate[B,CC[B]],A1,A2](tuple: (CC[A1], Iterable[A2]))IterableOps[CC,A1,A2]
scala>
scala> tupleOfIterableWrapper((List(1, 2, 3), List(6, 5, 4))) res0: IterableOps[List,Int,Int] = IterableOps@439b2c10
cheers,adriaan
On Wed, May 27, 2009 at 2:32 PM, Adriaan Moors <adriaan.moors@cs.kuleuven.be> wrote:
My first prototype for tcpoly inference is available at http://github.com/adriaanm/scala/tree/tcpolyinfer
More test cases (or bug reports/fixes ;-)) greatly appreciated!
Behold:
Welcome to Scala version 2.8.0.r0-b20090602175319 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_07). Type in expressions to have them evaluated. Type :help for more information.
scala> def test[CC[+X] <: Iterable[X], A](xs: CC[A]): CC[A] = xs test: [CC[+X] <: Iterable[X],A](xs: CC[A])CC[A]
scala> val xs = test(List(1,2)) // List type constructor omitted xs: List[Int] = List(1, 2) // TADA
scala> val xs2: List[Int] = test(List(1,2)) xs2: List[Int] = List(1, 2)
scala> import scala.collection.generic._ import scala.collection.generic._
scala>
scala> class IterableOps[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2])) defined class IterableOps
scala> def tupleOfIterableWrapper[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2])) = new IterableOps[CC, A1, A2](tuple) tupleOfIterableWrapper: [CC[+B] <: Iterable[B] with scala.collection.generic.IterableTemplate[B,CC[B]],A1,A2](tuple: (CC[A1], Iterable[A2]))IterableOps[CC,A1,A2]
scala>
scala> tupleOfIterableWrapper((List(1, 2, 3), List(6, 5, 4))) res0: IterableOps[List,Int,Int] = IterableOps@439b2c10
cheers,adriaan
On Wed, May 27, 2009 at 2:32 PM, Adriaan Moors <adriaan.moors@cs.kuleuven.be> wrote:
I think that's one of the known problems with inference for hk types.I'm currently working on implementing type constructor inference -- still a little jetlagged, but I hope to have a proof of concept very soon.
cheersadriaan
Wed, 2009-06-03, 13:07
#8
Re: map2/map3
On Wed, Jun 03, 2009 at 10:25:15AM +0200, Adriaan Moors wrote:
> My first prototype for tcpoly inference is available at
> http://github.com/adriaanm/scala/tree/tcpolyinfer
Awesome! I look forward to put it through the paces. Can you comment on
your overall feeling about it, i.e. do you think it's close to something
which could enter the mainline, or does "prototype" imply long distance?
Wed, 2009-06-03, 18:07
#9
Re: map2/map3
On Wed, Jun 3, 2009 at 1:59 PM, Paul Phillips <paulp@improving.org> wrote:
On Wed, Jun 03, 2009 at 10:25:15AM +0200, Adriaan Moors wrote:I think it could enter trunk soon. Currently working on cleaning up the code, adding docs, and trying more examples. The patch in itself is fairly small, so I'm hopeful :-)
> My first prototype for tcpoly inference is available at
> http://github.com/adriaanm/scala/tree/tcpolyinfer
Awesome! I look forward to put it through the paces. Can you comment on
your overall feeling about it, i.e. do you think it's close to something
which could enter the mainline, or does "prototype" imply long distance?
adriaan
Wed, 2009-06-03, 20:37
#10
Re: map2/map3
This is just great Adriaan!!!
Of all people I can think, Tony will probably be the happiest with this development.
CKKL
On Wed, Jun 3, 2009 at 20:00, Adriaan Moors <adriaan.moors@cs.kuleuven.be> wrote:
--
__~O
-\ <, Christos KK Loverdos
(*)/ (*) http://ckkloverdos.com
Of all people I can think, Tony will probably be the happiest with this development.
CKKL
On Wed, Jun 3, 2009 at 20:00, Adriaan Moors <adriaan.moors@cs.kuleuven.be> wrote:
On Wed, Jun 3, 2009 at 1:59 PM, Paul Phillips <paulp@improving.org> wrote:
On Wed, Jun 03, 2009 at 10:25:15AM +0200, Adriaan Moors wrote:I think it could enter trunk soon. Currently working on cleaning up the code, adding docs, and trying more examples. The patch in itself is fairly small, so I'm hopeful :-)
> My first prototype for tcpoly inference is available at
> http://github.com/adriaanm/scala/tree/tcpolyinfer
Awesome! I look forward to put it through the paces. Can you comment on
your overall feeling about it, i.e. do you think it's close to something
which could enter the mainline, or does "prototype" imply long distance?
adriaan
--
__~O
-\ <, Christos KK Loverdos
(*)/ (*) http://ckkloverdos.com
Wed, 2009-06-03, 20:47
#11
Re: map2/map3
On Wednesday June 3 2009, Christos KK Loverdos wrote:
> On Wed, Jun 3, 2009 at 20:00, Adriaan Moors wrote:
> > On Wed, Jun 3, 2009 at 1:59 PM, Paul Phillips wrote:
> >> On Wed, Jun 03, 2009 at 10:25:15AM +0200, Adriaan Moors wrote:
> >> > My first prototype for tcpoly inference is available at
> >> > http://github.com/adriaanm/scala/tree/tcpolyinfer
> >>
> >> Awesome! I look forward to put it through the paces. Can you
> >> comment on your overall feeling about it, i.e. do you think it's
> >> close to something which could enter the mainline, or does
> >> "prototype" imply long distance?
> >
> > I think it could enter trunk soon. Currently working on cleaning up
> > the code, adding docs, and trying more examples.
> > The patch in itself is fairly small, so I'm hopeful :-)
> >
> > adriaan
>
> This is just great Adriaan!!!
> Of all people I can think, Tony will probably be the happiest with this development.
>
> CKKL
[01:29:26] seems like Adriaan Moors is ahead of the curve regarding VCS when compared to the EPFL guys
[01:30:18] regarding VCS? what does that mean?
[01:30:56] dibblego: he posted the prototype of type constructor inference.. and he used github for it
[01:31:06] ah
[01:31:14] wait, type constructor inference?
[01:31:29] dibblego: yes, scala-internals
[01:31:43] map2/map3 thread
[01:31:51] I didn't think that was ever on the agenda
[01:31:54] dibblego: I thought you might be interested ;)
[01:32:00] cheers :)
(Times are PDT == GMT - 0700)
RRS
Fri, 2009-06-05, 17:07
#12
Re: map2/map3
First, some bad news:
Somehow, the algorithm would need to "break apart" that type (essentially function application at the type level) to recover the arguments to which this function was applied. It's kind of like guessing 1 and 2 (or 0 and 3, or 42 and -39, or...) from the constraint 3 == A + B, except it's much worse (given subtyping, abstract types, ...). At least, that's how I understand type inference at the moment...
The good news:
Welcome to Scala version 2.8.0.r0-b20090605174254 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_07). Type in expressions to have them evaluated.Type :help for more information.
scala> List((1,2),(3,4),(5,6)) unzip res0: (List[Int], List[Int]) = (List(1, 3, 5),List(2, 4, 6))
Here's how I propose to implement unzip:
trait TraversableClass[+A, +CC[X] <: Traversable[X]] {// ... def genericBuilder[B]: Builder[B, CC[B]] = companion.newBuilder[B]
// @M TODO: redundant... def foreach[U](f: A => U): Unit implicit def subtype_witness[T <: U, U](x: T): U = x // @M TODO: move to Predef? def unzip[X, Y](implicit c: A => (X, Y)): (CC[X], CC[Y]) = { val b1 = genericBuilder[X] val b2 = genericBuilder[Y] foreach { case (x1: X, x2: Y) => b1 += x1 b2 += x2 } (b1.result, b2.result) } }
I'm sure the design can be improved, but I highlighted the essence. (See the flatten example on pp. 52--53, Section 3.6.4, of my thesis for details.)
cheers,adriaan
scala> pairTraversableWrapper(List((1,2),(3,4),(5,6)))I don't think we (I) can (will/want to/...) solve this kind of type inference problem, as it requires the type checker to unify something like ?CC[(?A, ?B)] with the type List[(Int, Int)].
<console>:8: error: inferred type arguments [List[(Int, Int)],A1,A2] do not conform to method pairTraversableWrapper's type parameter bounds [This <: Traversable[(A1, A2)],A1,A2]
pairTraversableWrapper(List((1,2),(3,4),(5,6)))
^
Somehow, the algorithm would need to "break apart" that type (essentially function application at the type level) to recover the arguments to which this function was applied. It's kind of like guessing 1 and 2 (or 0 and 3, or 42 and -39, or...) from the constraint 3 == A + B, except it's much worse (given subtyping, abstract types, ...). At least, that's how I understand type inference at the moment...
The good news:
Welcome to Scala version 2.8.0.r0-b20090605174254 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_07). Type in expressions to have them evaluated.Type :help for more information.
scala> List((1,2),(3,4),(5,6)) unzip res0: (List[Int], List[Int]) = (List(1, 3, 5),List(2, 4, 6))
Here's how I propose to implement unzip:
trait TraversableClass[+A, +CC[X] <: Traversable[X]] {// ... def genericBuilder[B]: Builder[B, CC[B]] = companion.newBuilder[B]
// @M TODO: redundant... def foreach[U](f: A => U): Unit implicit def subtype_witness[T <: U, U](x: T): U = x // @M TODO: move to Predef? def unzip[X, Y](implicit c: A => (X, Y)): (CC[X], CC[Y]) = { val b1 = genericBuilder[X] val b2 = genericBuilder[Y] foreach { case (x1: X, x2: Y) => b1 += x1 b2 += x2 } (b1.result, b2.result) } }
I'm sure the design can be improved, but I highlighted the essence. (See the flatten example on pp. 52--53, Section 3.6.4, of my thesis for details.)
cheers,adriaan
Mon, 2009-06-08, 10:27
#13
Re: map2/map3
Good news everybody! I spoke to soon in my message on Friday. Colin's approach works in my tcpolyinfer prototype:
Welcome to Scala version 2.8.0.r0-b20090605174254 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_07). Type in expressions to have them evaluated.Type :help for more information.
scala> import scala.collection.generic._ import scala.collection.generic._
scala> class IterableOps[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2]))
defined class IterableOps
scala> implicit def tupleOfIterableWrapper[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2])) | = new IterableOps[CC, A1, A2](tuple)tupleOfIterableWrapper: [CC[+B] <: Iterable[B] with scala.collection.generic.IterableTemplate[B,CC[B]],A1,A2](tuple: (CC[A1], Iterable[A2]))IterableOps[CC,A1,A2]
scala> val t = (List(1, 2, 3), List(6, 5, 4)) t: (List[Int], List[Int]) = (List(1, 2, 3),List(6, 5, 4))
scala> tupleOfIterableWrapper(t) res0: IterableOps[List,Int,Int] = IterableOps@87409cf
That said, I still kind of prefer my approach, which encodes generalised constraints using a universal implicit that serves as a witness for an arbitrary subtyping constraint. I think the optimiser should be able to eliminate this implicit witness (since it's just the constrained polymorphic identity function), so that we get generalised constraints for free in Scala (without the pretty syntax, though)
cheers,adriaan
On Tue, May 19, 2009 at 6:10 PM, Colin Bullock <cmbullock@gmail.com> wrote:
Welcome to Scala version 2.8.0.r0-b20090605174254 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_07). Type in expressions to have them evaluated.Type :help for more information.
scala> import scala.collection.generic._ import scala.collection.generic._
scala> class IterableOps[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2]))
defined class IterableOps
scala> implicit def tupleOfIterableWrapper[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2](tuple: (CC[A1], Iterable[A2])) | = new IterableOps[CC, A1, A2](tuple)tupleOfIterableWrapper: [CC[+B] <: Iterable[B] with scala.collection.generic.IterableTemplate[B,CC[B]],A1,A2](tuple: (CC[A1], Iterable[A2]))IterableOps[CC,A1,A2]
scala> val t = (List(1, 2, 3), List(6, 5, 4)) t: (List[Int], List[Int]) = (List(1, 2, 3),List(6, 5, 4))
scala> tupleOfIterableWrapper(t) res0: IterableOps[List,Int,Int] = IterableOps@87409cf
That said, I still kind of prefer my approach, which encodes generalised constraints using a universal implicit that serves as a witness for an arbitrary subtyping constraint. I think the optimiser should be able to eliminate this implicit witness (since it's just the constrained polymorphic identity function), so that we get generalised constraints for free in Scala (without the pretty syntax, though)
cheers,adriaan
On Tue, May 19, 2009 at 6:10 PM, Colin Bullock <cmbullock@gmail.com> wrote:
Using the following definitions:
class IterableOps
[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2]
(tuple: (CC[A1], Iterable[A2]))
implicit def tupleOfIterableWrapper
[CC[+B] <: Iterable[B] with IterableTemplate[B, CC[B]], A1, A2]
(tuple: (CC[A1], Iterable[A2]))
= new IterableOps[CC, A1, A2](tuple)
I get the following:
scala> val t = (List(1, 2, 3), List(6, 5, 4))
t: (List[Int], List[Int]) = (List(1, 2, 3), List(6, 5, 4))
scala> tupleOfIterableWrapper[List, Int, Int](t)
res3: IterableOps[List, Int, Int] = IterableOps@2365fe
scala> tupleOfIterableWrapper(t)
<console>:17: error: inferred kinds of the type arguments (List[Int],A1,Int) do not conform to the expected kinds of the type parameters (type CC,type A1,type A2).
List[Int]'s type parameters do not match type CC's expected parameters: class List has one type parameter, but type CC has one
tupleOfIterableWrapper(t)
^
<console>:17: error: type mismatch;
found : (List[Int], List[Int])
required: (List[A1], Iterable[Int])
tupleOfIterableWrapper(t)
^
The error message of "class List has one type parameter, but type CC has one" certainly seems suspicious there...
- Colin
Disclaimer: http://www.kuleuven.be/cwis/email_disclaimer.htm for more information.
Wed, 2009-06-17, 14:57
#14
Re: map2/map3
On Fri, Jun 5, 2009 at 6:02 PM, Adriaan
Moors wrote:
> First, some bad news:
>
>>
>> scala> pairTraversableWrapper(List((1,2),(3,4),(5,6)))
>> :8: error: inferred type arguments [List[(Int, Int)],A1,A2] do
>> not conform to method pairTraversableWrapper's type parameter bounds [This
>> <: Traversable[(A1, A2)],A1,A2]
>> pairTraversableWrapper(List((1,2),(3,4),(5,6)))
>> ^
>
> I don't think we (I) can (will/want to/...) solve this kind of type
> inference problem, as it requires the type checker to unify something like
> ?CC[(?A, ?B)] with the type List[(Int, Int)].
> Somehow, the algorithm would need to "break apart" that type (essentially
> function application at the type level) to recover the arguments to which
> this function was applied. It's kind of like guessing 1 and 2 (or 0 and 3,
> or 42 and -39, or...) from the constraint 3 == A + B, except it's much worse
> (given subtyping, abstract types, ...).
> At least, that's how I understand type inference at the moment...
> The good news:
> Welcome to Scala version 2.8.0.r0-b20090605174254 (Java HotSpot(TM) 64-Bit
> Server VM, Java 1.6.0_07).
> Type in expressions to have them evaluated.
> Type :help for more information.
> scala> List((1,2),(3,4),(5,6)) unzip
> res0: (List[Int], List[Int]) = (List(1, 3, 5),List(2, 4, 6))
> Here's how I propose to implement unzip:
> trait TraversableClass[+A, +CC[X] <: Traversable[X]] {
> // ...
> def genericBuilder[B]: Builder[B, CC[B]] = companion.newBuilder[B]
>
> // @M TODO: redundant...
> def foreach[U](f: A => U): Unit
>
> implicit def subtype_witness[T <: U, U](x: T): U = x // @M TODO: move to
> Predef?
> def unzip[X, Y](implicit c: A => (X, Y)): (CC[X], CC[Y]) = {
> val b1 = genericBuilder[X]
> val b2 = genericBuilder[Y]
> foreach { case (x1: X, x2: Y) =>
> b1 += x1
> b2 += x2
> }
> (b1.result, b2.result)
> }
> }
> I'm sure the design can be improved, but I highlighted the essence. (See the
> flatten example on pp. 52--53, Section 3.6.4, of my thesis for details.)
> cheers,
Yes, that looks like the way to go. One small remark: You don't need
subtypeWitness; the identity implicit is already defined in Predef.
I verified that unzip and flatten compile that way. The next challenge will be
Tuple2.map and friends.
Cheers
It might well be that you are right. I'll have to investigate wqhen I
have a bit more time.