Code Examples for

Programming in Scala, Fourth Edition

Return to chapter index

9 Control Abstraction

Sample run of chapter's interpreter examples

9.1 Reducing code duplication


// In file control-abstraction/Files1.scala object FileMatcher { private def filesHere = (new java.io.File(".")).listFiles def filesEnding(query: String) = for (file <- filesHere; if file.getName.endsWith(query)) yield file }
// In file control-abstraction/Files1.scala def filesContaining(query: String) = for (file <- filesHere; if file.getName.contains(query)) yield file
// In file control-abstraction/Files1.scala def filesRegex(query: String) = for (file <- filesHere; if file.getName.matches(query)) yield file
def filesMatching(query: String, method) = for (file <- filesHere; if file.getName.method(query)) yield file
// In file control-abstraction/Files2.scala def filesMatching(query: String, matcher: (String, String) => Boolean) = { for (file <- filesHere; if matcher(file.getName, query)) yield file }
// In file control-abstraction/Files2.scala def filesEnding(query: String) = filesMatching(query, _.endsWith(_)) def filesContaining(query: String) = filesMatching(query, _.contains(_)) def filesRegex(query: String) = filesMatching(query, _.matches(_))
(fileName: String, query: String) => fileName.endsWith(query)
// In file control-abstraction/Files.scala object FileMatcher { private def filesHere = (new java.io.File(".")).listFiles private def filesMatching(matcher: String => Boolean) = for (file <- filesHere; if matcher(file.getName)) yield file def filesEnding(query: String) = filesMatching(_.endsWith(query)) def filesContaining(query: String) = filesMatching(_.contains(query)) def filesRegex(query: String) = filesMatching(_.matches(query)) }

9.2 Simplifying client code


def containsNeg(nums: List[Int]): Boolean = { var exists = false for (num <- nums) if (num < 0) exists = true exists }
scala> containsNeg(List(1, 2, 3, 4)) res0: Boolean = false scala> containsNeg(List(1, 2, -3, 4)) res1: Boolean = true
def containsNeg(nums: List[Int]) = nums.exists(_ < 0)
scala> containsNeg(Nil) res2: Boolean = false scala> containsNeg(List(0, -1, -2)) res3: Boolean = true
def containsOdd(nums: List[Int]): Boolean = { var exists = false for (num <- nums) if (num % 2 == 1) exists = true exists }
def containsOdd(nums: List[Int]) = nums.exists(_ % 2 == 1)

9.3 Currying


scala> def plainOldSum(x: Int, y: Int) = x + y plainOldSum: (x: Int, y: Int)Int scala> plainOldSum(1, 2) res4: Int = 3
scala> def curriedSum(x: Int)(y: Int) = x + y curriedSum: (x: Int)(y: Int)Int scala> curriedSum(1)(2) res5: Int = 3
scala> def first(x: Int) = (y: Int) => x + y first: (x: Int)Int => Int
scala> val second = first(1) second: Int => Int = $$Lambda$1044/1220897602@5c6fae3c
scala> second(2) res6: Int = 3
scala> val onePlus = curriedSum(1)_ onePlus: Int => Int = $$Lambda$1054/711248671@3644d12a
scala> onePlus(2) res7: Int = 3
scala> val twoPlus = curriedSum(2)_ twoPlus: Int => Int = $$Lambda$1055/473485349@48b85dc5 scala> twoPlus(2) res8: Int = 4

9.4 Writing new control structures


scala> def twice(op: Double => Double, x: Double) = op(op(x)) twice: (op: Double => Double, x: Double)Double scala> twice(_ + 1, 5) res9: Double = 7.0
// In file control-abstraction/WithPrintWriter1.scala def withPrintWriter(file: File, op: PrintWriter => Unit) = { val writer = new PrintWriter(file) try { op(writer) } finally { writer.close() } }
// In file control-abstraction/WithPrintWriter1.scala withPrintWriter( new File("date.txt"), writer => writer.println(new java.util.Date) )
scala> println("Hello, world!") Hello, world!
scala> println { "Hello, world!" } Hello, world!
scala> val g = "Hello, world!" g: String = Hello, world! scala> g.substring { 7, 9 } ^ error: ';' expected but ',' found.
scala> g.substring(7, 9) res12: String = wo
// In file control-abstraction/WithPrintWriter2.scala def withPrintWriter(file: File)(op: PrintWriter => Unit) = { val writer = new PrintWriter(file) try { op(writer) } finally { writer.close() } }
// In file control-abstraction/WithPrintWriter2.scala val file = new File("date.txt") withPrintWriter(file) { writer => writer.println(new java.util.Date) }

9.5 By-name parameters


withPrintWriter(file) { writer => writer.println(new java.util.Date) }
// In file control-abstraction/Assert.scala var assertionsEnabled = true def myAssert(predicate: () => Boolean) = if (assertionsEnabled && !predicate()) throw new AssertionError
myAssert(() => 5 > 3)
myAssert(5 > 3) // Won't work, because missing () =>
// In file control-abstraction/Assert.scala def byNameAssert(predicate: => Boolean) = if (assertionsEnabled && !predicate) throw new AssertionError
byNameAssert(5 > 3)
def boolAssert(predicate: Boolean) = if (assertionsEnabled && !predicate) throw new AssertionError
boolAssert(5 > 3)
scala> val x = 5 x: Int = 5 scala> assertionsEnabled = false mutated assertionsEnabled scala> boolAssert(x / 0 == 0) java.lang.ArithmeticException: / by zero ... 27 elided
scala> byNameAssert(x / 0 == 0)

9.6 Conclusion

For more information about Programming in Scala, Fourth Edition (the "Stairway Book"), please visit:

http://www.artima.com/shop/programming_in_scala_4ed

and:

http://booksites.artima.com/programming_in_scala_4ed

Copyright © 2007-2019 Artima, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.