I just found some notes from when I first began working with Scala, and I was working with the yield
keyword in for
loops. If you haven't worked with something like yield
before, it will help to know how it works. Also, when you need to do searches for problems, or when you want to talk to other Scala developers, it will also help to know that when you use the for
/yield
keywords as shown in these examples, you’re creating something known as a for expression.
How a “for expression” works
Here's a statement of how the yield
keyword works in for
loops, from the book, Programming in Scala:
For each iteration of your
for
loop,yield
generates a value which will be remembered. It's like thefor
loop has a buffer you can’t see, and for each iteration of yourfor
loop another item is added to that buffer. When yourfor
loop finishes running, it will return this collection of all the yielded values. The type of the collection that is returned is the same type that you were iterating over, so aMap
yields aMap
, aList
yields aList
, and so on.Also, note that the initial collection is not changed; the for/yield construct creates a new collection according to the algorithm you specify.
Basic Scala 'for' expression examples
Given that background information, let’s look at a few for/yield examples. First, this example just yields a new collection that’s identical to the collection I’m looping over:
scala> for (i <- 1 to 5) yield i res10: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5)
Nothing too exciting there, but it’s a start. Next, let’s double every element in our initial collection:
scala> for (i <- 1 to 5) yield i * 2 res11: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
As another example, here’s what the Scala modulus operator does in a for/yield loop:
scala> for (i <- 1 to 5) yield i % 2 res12: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 0, 1, 0, 1)
for/yield examples over a Scala Array
I mentioned in my description that the for
loop yield construct returns a collection that is the same as the collection it is given. To demonstrate this, let’s look at the same examples with a Scala Array
. Note the type of the collection that is yielded, and compare it to the previous examples:
scala> val a = Array(1, 2, 3, 4, 5) a: Array[Int] = Array(1, 2, 3, 4, 5) scala> for (e <- a) yield e res5: Array[Int] = Array(1, 2, 3, 4, 5) scala> for (e <- a) yield e * 2 res6: Array[Int] = Array(2, 4, 6, 8, 10) scala> for (e <- a) yield e % 2 res7: Array[Int] = Array(1, 0, 1, 0, 1)
As you can see, in these examples an Array[Int]
was yielded, while in the earlier examples an IndexedSeq[Int]
was returned.
'for' loop, yield, and guards (for-loop ‘if’ conditions)
If you’re familiar with the Scala for comprehension syntax, you know that you can add if
statements to your for loop construct. Tests like these are often referred to as “guards,” and you can combine them with the yield
syntax, as shown here:
scala> val a = Array(1, 2, 3, 4, 5) a: Array[Int] = Array(1, 2, 3, 4, 5) scala> for (e <- a if e > 2) yield e res1: Array[Int] = Array(3, 4, 5)
As you can see, adding the if e > 2
guard condition limits the Array
we return to the three elements shown.
A real-world example
I don’t know if this will make sense out of context, but if you’d like to see a real-world use of a for/yield loop, here you go:
def getQueryAsSeq(query: String): Seq[MiniTweet] = { val queryResults = getTwitterInstance.search(new Query(query)) val tweets = queryResults.getTweets // java.util.List[Status] for (status <- tweets) yield ListTweet(status.getUser.toString, status.getText, status.getCreatedAt.toString) }
This code uses the JavaConversions
package to convert the java.util.List[Status]
I get back from Twitter4J into a Seq[MiniTweet]
. The loop actually returns a Buffer[ListTweet]
, which is a Seq[ListTweet]
. A MiniTweet
is just a small version of a Twitter tweet, with the three fields shown.
Summary: Scala for-loop and yield examples
If you’re familiar with Scala’s for-loop construct, you know that there’s also much more work that can be performed in the first set of parentheses. You can add if
statements and other statements there, such as this example from the book Programming in Scala:
def scalaFiles = for { file <- filesHere if file.getName.endsWith(".scala") } yield file
I'll try to share more complicated examples like this in the future, but for today I wanted to share some simple for/yield examples, something like “An introduction to the Scala yield
keyword.”
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Summary: Scala’s ‘yield’ keyword
As a quick summary of the yield keyword:
- For each iteration of your for loop, yield generates a value which is remembered by the for loop (behind the scenes, like a buffer).
- When your for loop finishes running, it returns a collection of all these yielded values.
- The type of the collection that is returned is the same type that you were iterating over.
I hope these examples have been helpful.