I hope you’ve seen how using the Scala REPL to evaluate and experiment with code provides an enriched learning environment for this programming language. As you continue through this book, keep the REPL open and use it to validate everything you learn. The code samples throughout the book are presented as raw captures of REPL sessions to both validate that they work and what they print out, and also to make it easier for you to replicate them in your own REPL session.
Even better, modify and rework code examples until they break. Scala is a compiled, statically typed language, so the REPL (which compiles a line after you hit Return) will let you know immediately if you have entered incorrect Scala code or not. This will help you pick up the language more quickly and better understand the limits of its syntax and features.
This may be a challenging chapter to see through to the end, because you had to read all about types and data without learning how to do real programming in Scala yet. I’m glad you did.
What was the oddest or most-unexpected part of this chapter? The use of keywords to announce value and variable definition? The reversed manner (if you’re coming from Java) of defining a variable’s name before its type? The idea that much of your code can use fixed, nonreassignable values instead of (variable) variables?
If these ideas were hard to take, the good news is that, as you gain experience in Scala developemnt, they will become quite normal. Eventually they may even seem to be obvious choices for a well-designed functional programming language.
At this point you should know how to define your own values and variables, although we haven’t yet learned where to come up with useful data to store in them. In the next chapter you will study ways to derive and calculate this data using logical structures known as expressions.
Is there a simpler way to write the following?
val flag: Boolean = falseval result: Boolean = (flag == false)We covered if/else conditions, pattern matching, and loops in detail in this chapter. These structures provide a solid basis for writing core logic in Scala.
However, these three (or two) structures are just as important to learning Scala development as learning about the fundamentals of expressions. The namesake of this chapter—expressions and their return values—are the real core building block of any application. Expressions themselves may seem to be an obvious concept, and devoting an entire chapter to them has the appearance of being overly generous to the topic. The reason I have devoted a chapter to them is that learning to work in terms of expressions is a useful and valuable skill. You should consider expressions when writing code, and even structure your applications around them. Some important principles to keep in mind when writing expressions are (1) how you will organize your code as expressions, (2) how your expressions will derive a return value, and (3) what you will do with that return value.
Expressions, in addition to being a foundation for your code organization, are also a foundation for Scala’s syntax. In this chapter we have defined if/else conditions, pattern matching, and loops in terms of how they are structured around expressions. In the next chapter we will continue this practice by introducing functions as named, reusable expressions and defining them as such. Future chapters will continue this trend of defining concepts and structures in terms of expressions. Thus, understanding the basic nature and syntax of expressions and expression blocks is a crucial key to picking up the syntax for the rest of the language.
While the Scala REPL provides an excellent venue for experimenting with the language’s features, writing more than a line or two of code in it can be challenging. Because you’ll need to start working with more than a few lines of code, it’s time to start working in standalone Scala source files.
The scala command, which launches the Scala REPL, can also be used to evaluate and execute Scala source files:
To test this out, create a new file titled Hello.scala with the following contents:
Then execute it with the scala command:
You should see the result (“Hello, World”) printed on the next line.
An alternate way to execute external Scala files is with the :load command in the Scala REPL. This is useful if you want to stay in the Scala REPL while still using a text editor or IDE to edit your code.
To test this out, in the same directory you created the Hello.scala file, start the Scala REPL and run :load Hello.scala:
Because most of the language’s logical structures were covered in Chapter 3, it made sense to focus this, the chapter following expressions, on how to organize and reuse them as functions. Indeed, while a function’s name, input parameters, and return value type are important parts of a function’s definition, the actual contents of a function are one big expression.
An entire chapter about functions should be no less than expected from a book itself devoted to a functional programming language. However, though you just finished an entire chapter about functions, you have not yet learned everything there is to know about functions. Namely, that in Scala you can treat your functions as data and pass them into other functions to invoke.
This concept of functions as data, with their own literals and types, brings functions up to par with other forms as data. You’ll learn all about how functions can receive the same treatment as other data types, making them “first-class” citizens of the language, in the next chapter.
Scala treats functions as first-class data types, as demonstrated throughout this chapter and supported by the notions of higher-order functions, function literals, and function types. While simply stated, until you have some experience working with first-class functions you may find the concept a difficult one to understand. If you haven’t already done so, I highly recommend trying out the code samples and experimenting with writing your own first-class function-based code. And then, after you have become familiar with storing functions as data and using them to invoke higher-order functions, you’ll find the following exercises will help to increase your comfort level with this challenging topic.
The real beauty and utility of higher-order functions, however, cannot be demonstrated with the data types we have thus far learned. To really demonstrate them we will need to learn a critical component of writing any kind of useful and data-driven code. I’m talking about collections, data structures that scale from zero to many elements and make it possible to collect multiple values of a given type. From lists to maps, Scala not only supports the data structures that you’re well familiar with, but provides ample use of higher-order functions to maximize your productivity. We’ll cover not only creating and iterating through collections, but how you’ll use map(), reduce(), and filter()to manage them with amazingly expressive code.
From this point forward you can expect to see first-class functions and higher-order functions play a prominent role in code examples and exercises. Whether demonstrated with the data types we have learned thus far or with the higher-order function-based collections library, these are the shining stars of the Scala language.
Let’s say that you happened to run across this function while reviewing another developer’s code:
What does this function accomplish? Can you give an example of how you might invoke it?
There’s a function named “square” that you would like to store in a function value. Is this the right way to do it? How else can you store a function in a value?
Do you recall the “typesafe” challenge from the exercises in Chapter 3? There is a popular coding interview question I’ll call “typesafe,” in which the numbers 1-100 must be printed one per line. The catch is that multiples of 3 must replace the number with the word “type,” while multiples of 5 must replace the number with the word “safe.” Of course, multiples of 15 must print “typesafe.”
Use the “conditional” function from exercise 6 to implement this challenge.
Would your solution be shorter if the return type of “conditional” did not match the type of the parameter x? Experiment with an altered version of the “conditional” function that works better with this challenge.
Working with collections, whether creating, mapping, filtering, or performing other operations, is a major component of software development. And lists, maps, and sets, some of the main building blocks for scalable data structures, are included as part of the default libraries for Java, Ruby, Python, PHP, and C++. What sets Scala’s collections library apart from the others is its core support for immutable data structures and higher-order operations.
The core data structures in Scala, List, Map, and Set, are immutable. They cannot be resized, nor can their contents be swapped out. As a way of giving precedence over mutable collections, their package (collection.immutable) is automatically imported into Scala namespaces by default. This precedence aims to steer developers toward the immutable collections and immutable data in general, a “best practice” in functional programming circles. This is not to say that mutable collections are less powerful or less capable than immutable ones. Scala’s mutable collections have all the same features as the immutable ones and also support a range of modification operations. We’ll learn about mutable collections and how to convert mutable to immutable ones (and vice versa) in the next chapter.
The ability to take a collection and iterate or map it with an anonymous function is common to many languages, including Ruby and Python. However, the ability to do so while ensuring the type requirements of both the collection and the input and return types of the anonymous functions is relatively uncommon. Collections with type-safe higher-order functions support a declarative programming style, the ability to create expressive code, and very few runtime type conversion errors. This powerful combination of features helps to set the Scala collections library apart from those available in other languages and frameworks and provides a fairly large productivity boost to its users. In addition, Scala collections are monadic, supporting the ability to chain operations together in a high-level, type-safe manner. We’ll learn about monadic collections as well in the next chapter.
Do you recall the suggestion I previously made (see Exercises) to switch your development environment from inside-the-REPL to an external Scala source file? If you haven’t made the switch yet, you’ll find working on these exercises in the REPL to be downright impractical given their size and complexity.
I also recommend working on these exercises using a professional IDE such as IntelliJ IDEA CE or the Eclipse-based Scala IDE. You’ll gain instant feedback about whether code is compilable and get code completion and documentation for Scala library functions. There are also plug-ins for simpler editing environments like Sublime Text, VIM, and Emacs that enable this functionality, but if you’re getting started with Scala a full-fledged IDE will probably be easier and quicker to use.
The exercises in this section will help you become familiar with the core collections and operations we have studied in this chapter. I recommend spending time to not only write the most basic solution, but to find alternate methods for each implementation. This will help you become familiar with the subtle differences between similar functions such as fold and reduce, or head and slice, in addition to giving you the tools to bypass these functions and develop your own solutions.
Write a function titled “factors” that takes a number and returns a list of its factors, other than 1 and the number itself. For example,factors(15) should return List(3, 5).
Then write a new function that applies “factors” to a list of numbers. Try using the list of Long numbers you generated in exercise 1. For example, executing this function with List(9, 11, 13, 15) should return List(3, 3, 5), because the factor of 9 is 3 while the factors of 15 are 3 again and 5. Is this a good place to use map and flatten? Or would a for-loop be a better fit?
The last exercise in this chapter is a multipart problem. We’ll be reading and processing a forecast from the excellent and free OpenWeatherMap API.
To read content from the URL we’ll use the Scala library operation io.Source.+fromURL(url: String), which returns an +io.Sourceinstance. Then we’ll reduce the source to a collection of individual lines using the getLines.toList operation. Here is an example of usingio.Source to read content from a URL, separate it into lines, and return the result as a list of strings:
Here is the URL we will use to retrieve the weather forecast, in XML format:
Go ahead and read this URL into a list of strings. Once you have it, print out the first line to verify you’ve captured an XML file. The result should look pretty much like this:
If you don’t see an XML header, make sure that your URL is correct and your Internet connection is up.
Let’s begin working with this List[String] containing the XML document.
The forecast’s city’s name is there in the first 10 lines. Grab it from the correct line and print out its XML element. Then extract the city name and country code from their XML elements and print them out together (e.g., “Paris, FR”). This is a good place to use regular expressions to extract the text from XML tags (see Regular expressions).
If you don’t want to use regular expression capturing groups, you could instead use the replaceAll() operation on strings to remove the text surrounding the city name and country name.
The “symbol” XML element in each forecast segment includes a description of the weather forecast. Extract this element in the same way you extracted the city name and country code. Try iterating through the forecasts, printing out the description.
Then create an informal weather report by printing out the weather descriptions over the next 12 hours (not including the XML elements).
Now that you have solved the exercises, are there simpler or shorter solutions than the ones you chose? Did you prefer infix dot notation or infix operator notation? Was using for..yield easier than higher-order operations like map and filter?
This is a good place to rework some of your solutions to really find your favored coding style, which is often the intersection between ease of writing, ease of reading, and expressiveness.
Mutable collections, well known and available in most programming languages, have the best of both worlds in Scala. They can be used as incremental buffers to expand collections one item at a time using buffers, builders, or other approaches, but also support the wide variety of operations available to immutable collections.
And collections are, especially as Scala broadly defines them, more than simple containers for application data. Monadic collections provide type-safe chainable operations and management for sensitive and complex situations such as missing data, error conditions, and concurrent processing.
In Scala, immutable, mutable, and monadic collections are indispensable building blocks and foundations for safe and expressive software development. They are ubiquitious in Scala code, and are generally applicable to a wide range of uses.
By learning and becoming familiar with the core operations of Iterable, and with the safe operation chaining of monadic collections, you can better leverage them as a core foundation for your applications in Scala.
This chapter concludes the Scala instructions for Part 1. In Part 2 we will cover object-oriented Scala, a core feature of this programming language, while continuing to use what we have learned thus far.
The Fibonacci series starts with the numbers “1, 1” and then computes each successive element as the sum of the previous two elements. We’ll use this series to get familiarized with the collections in this chapter.
Write a function to safely wrap calls to the JVM library method System.getProperty(<String>), avoiding raised exceptions or null results.System.getProperty(<String>) returns a JVM environment property value given the property’s name. For example,System.getProperty("java.home") will return the path to the currently running Java instance, while System. getProperty("user.timezone")returns the time zone property from the operating system. This method can be dangerous to use, however, because it may throw exceptions or return null for invalid inputs. Try invoking System.getProperty("") or System.getProperty("blah") from the Scala REPL to see how it responds.
Experienced Scala developers build their own libraries of functions that wrap unsafe code with Scala’s monadic collections. Your function should simply pass its input to the method and ensure that exceptions and null values are safely handled and filtered. Call your function with the example property names used here, including the valid and invalid ones, to verify that it never raises exceptions or returns null results.
Write a function that reports recent GitHub commits for a project. GitHub provides an RSS feed of recent commits for a given user, repository, and branch, containing XML that you can parse out with regular expressions. Your function should take the user, repository, and branch, read and parse the RSS feed, and then print out the commit information. This should include the date, title, and author of each commit.
You can use the following RSS URL to retrieve recent commits for a given repository and branch:
Here is one way to grab the RSS feed as a single string:
Working with the XML will be a bit tricky. You may want to use text.split(<token>) to split the text into the separate <entry>components, and then use regular expression capture groups (see Regular expressions) to parse out the <title> and other elements. You could also just try iterating through all the lines of the XML file, adding elements to a buffer as you find them, and then converting that to a new list.
Once you have completed this exercise (and there is a lot to do here), here are some additional features worth investigating:
Following exercise (c), mix the commits together and sort by commit date, then print your report with an additional “repo” column.
These additional features will take some time to implement, but are definitely worthwhile for learning and improving your Scala development skills.
Once you have finished these features, test out your commit report using entries from the following projects:
These features are all active (as of 2014), so you should see an interesting mix of commit activity data in your report. It’s worthwhile to browse the repositories for these core open source Scala projects, or at least their documentation, to understand some of the excellent work being done.
Write a command-line script to call your GitHub commit report function from exercise 6 and print out the results. This will require a Unix shell; if you are on a Windows system you will need a compatible Unix environment such as Cygwin or Virtualbox (running a Unix virtual machine). You’ll also need to install SBT (Simple Build Tool), a build tool that supports dependency management and plug-ins and is commonly used by Scala projects. You can download SBT from http://www.scala-sbt.org/ for any environment, including an MSI Windows Installer version. SBT is also available from popular package managers. If you are using Homebrew on OS X you can install it with brew install sbt.
扫码加好友,拉您进群



收藏
