Comprehension comprehension...

72 views
Skip to first unread message

Peter Wolf

unread,
Dec 23, 2016, 1:40:17 PM12/23/16
to scala-user
Hello and Happy Holidays!

I was teaching some new developers the Joy Of Comprehension

I showed them that this yields a List

   for { x <- List(1,2,3); y <- List(1,2,3) } yield x+y 

and this yields a Future

  for { x <- Future(1); y <- Future(1) } yield x+y 

Then I asked them what type this would yield

  for { x <- 1 to 3; y <- 1 to 3 } yield x+y

and the answer was...

  res3: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4, 3, 4, 5, 4, 5, 6)

Huh?  That not what I thought should happen...  I expected a range, or a compile error

I thought perhaps Vector extended Range, but this does not compile

  def foo(a: Range, b: Range): Range = for { x <- a; y <- b } yield x+y

I thought the whole point of Monads and for comprehension is that the type does not change.

Thoughts?

P

Patrick Roemer

unread,
Dec 23, 2016, 3:33:57 PM12/23/16
to scala...@googlegroups.com
Responding to Peter Wolf:
> Then I asked them what type this would yield
>
> for { x <- 1 to 3; y <- 1 to 3 } yield x+y
>
> and the answer was...
>
> res3: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4, 3, 4,
> 5, 4, 5, 6)
>
> Huh? That not what I thought should happen... I expected a range, or a
> compile error

How could it be a Range? What would start/end/step be for the result in
your example? And what if you replace "x+y" with "(x+y).toString"?

> I thought perhaps Vector extended Range, but this does not compile
>
> def foo(a: Range, b: Range): Range = for { x <- a; y <- b } yield x+y
>
> I thought the whole point of Monads and for comprehension is that the type
> does not change.

Range itself cannot be "monadic", since its element type is fixed - it's
not a type constructor of form M[A].

The "monadic" super type here basically is Traversable. But the Scala
collection API works hard to give you a closer matching type, thus you
end up with IndexedSeq, and the concrete instance happens to be a Vector.

Best regards,
Patrick


Peter Wolf

unread,
Dec 23, 2016, 3:57:44 PM12/23/16
to Patrick Roemer, scala-user
Cool.  

With a little thought, I got that Range could not be a Monad.  I did not understand how the compiler finds a common matching type and does comprehension there.

Here is another example

val x = for { x <- 1 to 2; y <- Set(1) } yield x+y
Vector(2, 3)




--
You received this message because you are subscribed to a topic in the Google Groups "scala-user" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scala-user/WH49O9DGf44/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scala-user+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Seth Tisue

unread,
Dec 23, 2016, 4:34:24 PM12/23/16
to scala-user
You can demonstrate this with less code, so e.g.

scala> (1 to 3).map(_.toString)
res0: scala.collection.immutable.IndexedSeq[String] = Vector(1, 2, 3)

Recall that for comprehensions are just sugar for calls to map, flatMap, and withFilter.

Patrick Roemer

unread,
Dec 23, 2016, 4:55:46 PM12/23/16
to scala...@googlegroups.com
Responding to Peter Wolf:
> With a little thought, I got that Range could not be a Monad. I did not
> understand how the compiler finds a common matching type and does
> comprehension there.

You can explore this in your IDE if you desugar.

val x = (1 to 3).flatMap(x => (1 to 3).map(y => x + y))

IDEA shows that map/flatMap is resolved to TraversableLike, which
returns an abstract That from the CanBuildFrom dance. The type hover on
x shows it's an IndexedSeq[Int]. And "Explain Scala code" shows that the
implicit argument to map/flatMap is IndexedSeq.canBuildFrom, indeed.

> val x = for { x <- 1 to 2; y <- Set(1) } yield x+y
>
> Vector(2, 3)

val y = for { y <- Set(1); x <- 1 to 2 } yield x+y
Set(2, 3)

:)

Best regards,
Patrick


som-snytt

unread,
Dec 28, 2016, 5:12:22 PM12/28/16
to scala-user, quellkris...@gmail.com

I recently said:

https://gitter.im/scala/contributors?at=585cbe64c02c1a3959a25260

to contrast


scala> ('a' to 'e').slice(1,3) res1: scala.collection.immutable.IndexedSeq[Char] = Vector(b, c) scala> ('a' to 'e').drop(1).take(2) res2: scala.collection.immutable.NumericRange[Char] = NumericRange b to c

and I said scaladoc promises a range here:

scala> ('a' to 'e').patch(1, 'x' to 'z', 2)
res6: scala.collection.immutable.IndexedSeq[Char] = Vector(a, x, y, z, d, e)

So promises and expectations are broken. It would be nice if user could find out when or why.

Also, our family nickname in the old country was Roemer, and my dog Roemer passed two years ago Christmas, so it's always a little jolt to see "Roemer". And the other dog was Padraig, Irish for Patrick. So I think I will go sing Auld Lang Syne now.
Reply all
Reply to author
Forward
0 new messages