This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Projections confusion

5 replies
Richard Dallaway
Joined: 2009-02-21,
User offline. Last seen 42 years 45 weeks ago.

I've having trouble getting my head around projections. I think :-/
If anyone can point me at an explanation of what's going on, that'd be
much appreciated.

Here's what I was playing with....

$ scala
Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java
1.5.0_16).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def pointlessVerboseTest(x:Int) = { println("Testing "+x); true }
pointlessVerboseTest: (Int)Boolean

scala> def ten() = for(i <- 1 to 10; if pointlessVerboseTest(i)) yield i
ten: ()Seq.Projection[Int]

When I call ten()(2) my Java head tells me I'd expect ten() to be
executed fully, printing out "Testing 1" through to "Testing 10" and
then I'd get the value 3 printed. What happens is...

scala> ten()(2)
Testing 1
Testing 2
Testing 3
res0: Int = 3

...which looks magical.

As I understand it, ten() is equivalent to:

new Range.Inclusive(1,10,1).filter(i=>pointlessVerboseTest(i)).map(v=>v)

Well...they both produce Seq.Projection[Int] = RangeFM(1, 2, 3, 4, 5,
6, 7, 8, 9, 10) which looks about right.

I can imagine how something clever is going on so that the production
of 1 to 10 is stopping at the right index, but even after digging
around the source I'm stuck on understanding what's driving this and
how filter and apply are working together to get the apparent result
of just calling filter on the first three elements of the range.

Many thanks
Richard

David Pollak
Joined: 2008-12-16,
User offline. Last seen 42 years 45 weeks ago.
Re: Projections confusion
Richard,
It's hard work being lazy and for strict programmers, understanding how to be lazy is often tougher. :-)
The projection that you defined is lazily evaluated.  That means that the call to filter which then calls pointlessVerboseTest is only evaluated when the item is needed, not when the projection is created.
So, when you apply 2 (ten()(2)) The first two elements (and one more for good measure) are evaluated.  Try this will 1 to Integer.MAX_VALUE.  It'd suck to have to create MAX_VALUE cells (and you'd run out of memory on a 32 bit machine).  But with projections and laziness, you only create what you need.
Make sense?  Help any?
Thanks,
David

On Sat, Feb 21, 2009 at 9:30 AM, Richard Dallaway <dallaway@gmail.com> wrote:
I've having trouble getting my head around projections. I think :-/   If anyone can point me at an explanation of what's going on, that'd be much appreciated.

Here's what I was playing with....

$ scala
Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java 1.5.0_16).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def pointlessVerboseTest(x:Int) = { println("Testing "+x); true }
pointlessVerboseTest: (Int)Boolean

scala> def ten() = for(i <- 1 to 10; if pointlessVerboseTest(i)) yield i
ten: ()Seq.Projection[Int]

When I call ten()(2) my Java head tells me I'd expect ten() to be executed fully, printing out "Testing 1" through to "Testing 10" and then I'd get the value 3 printed.  What happens is...

scala> ten()(2)
Testing 1
Testing 2
Testing 3
res0: Int = 3

...which looks magical.

As I understand it, ten() is equivalent to:

new Range.Inclusive(1,10,1).filter(i=>pointlessVerboseTest(i)).map(v=>v)

Well...they both produce Seq.Projection[Int] = RangeFM(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) which looks about right.

I can imagine how something clever is going on so that the production of 1 to 10 is stopping at the right index, but even after digging around the source I'm stuck on understanding what's driving this and how filter and apply are working together to get the apparent result of just calling filter on the first three elements of the range.

Many thanks
Richard





--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Git some: http://github.com/dpp
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Projections confusion

On Sat, Feb 21, 2009 at 05:30:14PM +0000, Richard Dallaway wrote:
> scala> ten()(2)
> Testing 1
> Testing 2
> Testing 3
> res0: Int = 3
>
> ...which looks magical.

Lazy.

> As I understand it, ten() is equivalent to:
>
> new Range.Inclusive(1,10,1).filter(i=>pointlessVerboseTest(i)).map(v=>v)
>
> Well...they both produce Seq.Projection[Int] = RangeFM(1, 2, 3, 4, 5, 6,
> 7, 8, 9, 10) which looks about right.

scala> (new Range.Inclusive(1,10,1).filter(i=>pointlessVerboseTest(i))(2))
Testing 1
Testing 2
Testing 3
res4: Int = 3

If you don't want laziness you can use force:

def ten() = for(i <- (1 to 10).force; if pointlessVerboseTest(i)) yield i

But because of laziness you need not stop at ten, you can go to infinity:

def lots() = for(i <- Stream.from(1); if pointlessVerboseTest(i)) yield i

scala> lots()(2)
Testing 1
Testing 2
Testing 3
res6: Int = 3

Will have to leave the mechanics of laziness to another day or another person (jamesiry the scala blog
terminator can usually be counted on here.)

vpatryshev
Joined: 2009-02-16,
User offline. Last seen 1 year 24 weeks ago.
Re: Projections confusion
Even in Java you can model lazy list:
class LazyList<T> {  Iterable<T> source;   LazyList<T>(Iterable<T> source) {
    this.source = source;  }    T get(int at) {     int current = 0;    for (T item : source) {       if (at == current++) return item;    }     throw NoSuchElementException();  } }

In this case, if your iterable is a generator that produces values on demand, it won't produce new values until requested. That's what is happening in your Scala code: you use yield, which means that execution of the loop code is suspended and the current i value is returned.
Iterable<Integer> ten = new Iterable<Integer>() {   new Iterator<Integer>() {    int i = 0;     boolean hasNext() { return i < 10; }    int next() { System.out.print("Testing " + i); return i; }   }}
2009/2/21 Richard Dallaway <dallaway@gmail.com>
I've having trouble getting my head around projections. I think :-/   If anyone can point me at an explanation of what's going on, that'd be much appreciated.

Here's what I was playing with....

$ scala
Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java 1.5.0_16).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def pointlessVerboseTest(x:Int) = { println("Testing "+x); true }
pointlessVerboseTest: (Int)Boolean

scala> def ten() = for(i <- 1 to 10; if pointlessVerboseTest(i)) yield i
ten: ()Seq.Projection[Int]

When I call ten()(2) my Java head tells me I'd expect ten() to be executed fully, printing out "Testing 1" through to "Testing 10" and then I'd get the value 3 printed.  What happens is...

scala> ten()(2)
Testing 1
Testing 2
Testing 3
res0: Int = 3

...which looks magical.

As I understand it, ten() is equivalent to:

new Range.Inclusive(1,10,1).filter(i=>pointlessVerboseTest(i)).map(v=>v)

Well...they both produce Seq.Projection[Int] = RangeFM(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) which looks about right.

I can imagine how something clever is going on so that the production of 1 to 10 is stopping at the right index, but even after digging around the source I'm stuck on understanding what's driving this and how filter and apply are working together to get the apparent result of just calling filter on the first three elements of the range.

Many thanks
Richard





--
Thanks,
-Vlad
Richard Dallaway
Joined: 2009-02-21,
User offline. Last seen 42 years 45 weeks ago.
Re: Projections confusion

Thank you David, Vlad and Paul: I'll go spend some time understanding
laziness.

Cheers
Richard

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Projections confusion

No yield does not suspend execution like Python generators, it returns
map() which returns a projection.

On 2/21/09, Paul Phillips wrote:
> On Sat, Feb 21, 2009 at 05:30:14PM +0000, Richard Dallaway wrote:
>> scala> ten()(2)
>> Testing 1
>> Testing 2
>> Testing 3
>> res0: Int = 3
>>
>> ...which looks magical.
>
> Lazy.
>
>> As I understand it, ten() is equivalent to:
>>
>> new Range.Inclusive(1,10,1).filter(i=>pointlessVerboseTest(i)).map(v=>v)
>>
>> Well...they both produce Seq.Projection[Int] = RangeFM(1, 2, 3, 4, 5, 6,
>> 7, 8, 9, 10) which looks about right.
>
> scala> (new Range.Inclusive(1,10,1).filter(i=>pointlessVerboseTest(i))(2))
> Testing 1
> Testing 2
> Testing 3
> res4: Int = 3
>
> If you don't want laziness you can use force:
>
> def ten() = for(i <- (1 to 10).force; if pointlessVerboseTest(i)) yield i
>
> But because of laziness you need not stop at ten, you can go to infinity:
>
> def lots() = for(i <- Stream.from(1); if pointlessVerboseTest(i)) yield i
>
> scala> lots()(2)
> Testing 1
> Testing 2
> Testing 3
> res6: Int = 3
>
> Will have to leave the mechanics of laziness to another day or another
> person (jamesiry the scala blog
> terminator can usually be counted on here.)
>
> --
> Paul Phillips | The important thing here is that the music is not in
> Imperfectionist | the piano. And knowledge and edification is not in the
> Empiricist | computer. The computer is simply an instrument whose
> pull his pi pal! | music is ideas. -- Alan Kay
>

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland