#1894 List method proposal: zip

Jens Tue 29 May 2012

zip is one of the standard functions on lists in functional programming languages. It would be very useful in Fantom also.

l1 := ["a", "b", "c"]
l2 := [1, 2, 3, 4]

l1.zip( l2 ) => [Pair("a", 1), Pair("b", 2), Pair("c", 3)]
l1.zipWith( l2, |a, b| { return a + b } ) => ["a1", "b2", "c3"]
l1.eachZip( l2, |a, b| { doSomething(a, b) } ) => No return value

The purpose is to iterate over two lists at the same time. This does not seem to be supported by the Fantom list in a simple way.

The standard version zip returns a list of tuples or pairs. Since Fantom does not have any tuples or generic pair class, it might be better no leave that version out for now.

The more general version zipWith uses a user supplied function to combine the elements. This would be very useful to have.

It might also be useful to have a eachZip, which does not collect a resulting list but is used for its side effects.

This standard behaviour when one list is longer that the other is to end the iteration when any of the lists doesn't have any more elements.

The types would be:

Obj?[] zip(L that)
Obj?[] zipWith(Obj?[] that, |V, Obj? -> Obj?| f)
Void eachZip(Obj?[] that, |V, Obj? -> Obj?| f)

It does not seem possible to get more precise types than that. Ideally the element type of that would match the type of the second argument to f.

brian Tue 29 May 2012

I agree this would be nice, but it wouldn't be all that useful with our existing type system because we don't have the sophistication to infer the type of the closure or return pairs. The current work around which is sort of awkward but functional is this:

l1 := ["a", "b", "c"]
l2 := [1, 2, 3, 4]

l1.each |v1, i|
{
  v2 := l2[i]
  ...
}

It requires an extra line, but keeps all the static typing and type inference clean. If we did add something like eachZip, then you could avoid that extra line, but would have to use explicit typing:

l1.eachZip |v1, Int v2|
{
  ...
}

Given that awkwardness, I'm not sure it makes sense to add into the standard API.

Yuri Strot Tue 29 May 2012

Such sophistication will be amazing. I'm dreaming about

List:
  T?[] map(|V item, Int index->T?| c)
  T? reduce(T? init, |T? reduction, V item, Int index->T?| c)
Map:
  T? reduce(T? init, |T? reduction, V val, K key->T?| c)
  K:T? map(|V val, K key->T?| c)

Jens Tue 29 May 2012

I'm dreaming about ...

Be careful what you dream about, it might become true!

http://www.scala-lang.org/api/current/index.html#scala.collection.Map

And your dream doesn't even get to the variance annotations. Wait until it gets to the higher kinded polymorphism!

The crudeness of the Fantom type system is both one of its weakest and strongest points.

Jens Tue 29 May 2012

it wouldn't be all that useful with our existing type system

Hm, I see.

The arguments for a zip that I can think about anyway are:

  • It documents intent: You immediately see that you are iterating over two lists.
  • Uniformity: You could iterate over non indexed collections in the same way (since there are no common interfaces for different collections this might not really apply).
  • If you want a list as the result you don't have to create that in a separate statement before the iteration.
  • Makes functional programmers feel at home.
  • A saved line is a saved line...

Login or Signup to reply.