- •Contents
- •List of Figures
- •List of Tables
- •List of Listings
- •Foreword
- •Foreword to the First Edition
- •Acknowledgments
- •Introduction
- •A Scalable Language
- •A language that grows on you
- •What makes Scala scalable?
- •Why Scala?
- •Conclusion
- •First Steps in Scala
- •Conclusion
- •Next Steps in Scala
- •Conclusion
- •Classes and Objects
- •Semicolon inference
- •Singleton objects
- •A Scala application
- •Conclusion
- •Basic Types and Operations
- •Some basic types
- •Literals
- •Operators are methods
- •Arithmetic operations
- •Relational and logical operations
- •Bitwise operations
- •Object equality
- •Operator precedence and associativity
- •Rich wrappers
- •Conclusion
- •Functional Objects
- •Checking preconditions
- •Self references
- •Auxiliary constructors
- •Method overloading
- •Implicit conversions
- •A word of caution
- •Conclusion
- •Built-in Control Structures
- •If expressions
- •While loops
- •For expressions
- •Match expressions
- •Variable scope
- •Conclusion
- •Functions and Closures
- •Methods
- •Local functions
- •Short forms of function literals
- •Placeholder syntax
- •Partially applied functions
- •Closures
- •Special function call forms
- •Tail recursion
- •Conclusion
- •Control Abstraction
- •Reducing code duplication
- •Simplifying client code
- •Currying
- •Writing new control structures
- •Conclusion
- •Composition and Inheritance
- •A two-dimensional layout library
- •Abstract classes
- •Extending classes
- •Invoking superclass constructors
- •Polymorphism and dynamic binding
- •Using composition and inheritance
- •Heighten and widen
- •Putting it all together
- •Conclusion
- •How primitives are implemented
- •Bottom types
- •Conclusion
- •Traits
- •How traits work
- •Thin versus rich interfaces
- •Example: Rectangular objects
- •The Ordered trait
- •Why not multiple inheritance?
- •To trait, or not to trait?
- •Conclusion
- •Packages and Imports
- •Putting code in packages
- •Concise access to related code
- •Imports
- •Implicit imports
- •Package objects
- •Conclusion
- •Assertions and Unit Testing
- •Assertions
- •Unit testing in Scala
- •Informative failure reports
- •Using JUnit and TestNG
- •Property-based testing
- •Organizing and running tests
- •Conclusion
- •Case Classes and Pattern Matching
- •A simple example
- •Kinds of patterns
- •Pattern guards
- •Pattern overlaps
- •Sealed classes
- •The Option type
- •Patterns everywhere
- •A larger example
- •Conclusion
- •Working with Lists
- •List literals
- •The List type
- •Constructing lists
- •Basic operations on lists
- •List patterns
- •First-order methods on class List
- •Methods of the List object
- •Processing multiple lists together
- •Conclusion
- •Collections
- •Sequences
- •Sets and maps
- •Selecting mutable versus immutable collections
- •Initializing collections
- •Tuples
- •Conclusion
- •Stateful Objects
- •What makes an object stateful?
- •Reassignable variables and properties
- •Case study: Discrete event simulation
- •A language for digital circuits
- •The Simulation API
- •Circuit Simulation
- •Conclusion
- •Type Parameterization
- •Functional queues
- •Information hiding
- •Variance annotations
- •Checking variance annotations
- •Lower bounds
- •Contravariance
- •Object private data
- •Upper bounds
- •Conclusion
- •Abstract Members
- •A quick tour of abstract members
- •Type members
- •Abstract vals
- •Abstract vars
- •Initializing abstract vals
- •Abstract types
- •Path-dependent types
- •Structural subtyping
- •Enumerations
- •Case study: Currencies
- •Conclusion
- •Implicit Conversions and Parameters
- •Implicit conversions
- •Rules for implicits
- •Implicit conversion to an expected type
- •Converting the receiver
- •Implicit parameters
- •View bounds
- •When multiple conversions apply
- •Debugging implicits
- •Conclusion
- •Implementing Lists
- •The List class in principle
- •The ListBuffer class
- •The List class in practice
- •Functional on the outside
- •Conclusion
- •For Expressions Revisited
- •For expressions
- •The n-queens problem
- •Querying with for expressions
- •Translation of for expressions
- •Going the other way
- •Conclusion
- •The Scala Collections API
- •Mutable and immutable collections
- •Collections consistency
- •Trait Traversable
- •Trait Iterable
- •Sets
- •Maps
- •Synchronized sets and maps
- •Concrete immutable collection classes
- •Concrete mutable collection classes
- •Arrays
- •Strings
- •Performance characteristics
- •Equality
- •Views
- •Iterators
- •Creating collections from scratch
- •Conversions between Java and Scala collections
- •Migrating from Scala 2.7
- •Conclusion
- •The Architecture of Scala Collections
- •Builders
- •Factoring out common operations
- •Integrating new collections
- •Conclusion
- •Extractors
- •An example: extracting email addresses
- •Extractors
- •Patterns with zero or one variables
- •Variable argument extractors
- •Extractors and sequence patterns
- •Extractors versus case classes
- •Regular expressions
- •Conclusion
- •Annotations
- •Why have annotations?
- •Syntax of annotations
- •Standard annotations
- •Conclusion
- •Working with XML
- •Semi-structured data
- •XML overview
- •XML literals
- •Serialization
- •Taking XML apart
- •Deserialization
- •Loading and saving
- •Pattern matching on XML
- •Conclusion
- •Modular Programming Using Objects
- •The problem
- •A recipe application
- •Abstraction
- •Splitting modules into traits
- •Runtime linking
- •Tracking module instances
- •Conclusion
- •Object Equality
- •Equality in Scala
- •Writing an equality method
- •Recipes for equals and hashCode
- •Conclusion
- •Combining Scala and Java
- •Using Scala from Java
- •Annotations
- •Existential types
- •Using synchronized
- •Compiling Scala and Java together
- •Conclusion
- •Actors and Concurrency
- •Trouble in paradise
- •Actors and message passing
- •Treating native threads as actors
- •Better performance through thread reuse
- •Good actors style
- •A longer example: Parallel discrete event simulation
- •Conclusion
- •Combinator Parsing
- •Example: Arithmetic expressions
- •Running your parser
- •Basic regular expression parsers
- •Another example: JSON
- •Parser output
- •Implementing combinator parsers
- •String literals and regular expressions
- •Lexing and parsing
- •Error reporting
- •Backtracking versus LL(1)
- •Conclusion
- •GUI Programming
- •Panels and layouts
- •Handling events
- •Example: Celsius/Fahrenheit converter
- •Conclusion
- •The SCells Spreadsheet
- •The visual framework
- •Disconnecting data entry and display
- •Formulas
- •Parsing formulas
- •Evaluation
- •Operation libraries
- •Change propagation
- •Conclusion
- •Scala Scripts on Unix and Windows
- •Glossary
- •Bibliography
- •About the Authors
- •Index
Section 32.3 |
Chapter 32 · Actors and Concurrency |
729 |
An actor will only process messages matching one of the cases in the partial function passed to receive. For each message in the mailbox, receive will first invoke isDefinedAt on the passed partial function to determine whether it has a case that will match and handle the message. The receive method will choose the first message in the mailbox for which isDefinedAt returns true, and pass that message to the partial function’s apply method. The partial function’s apply method will handle the message. For example, echoActor’s apply method will print "received message: " followed by the message object’s toString result. If the mailbox contains no message for which isDefinedAt returns true, the actor on which receive was invoked will block until a matching message arrives.
For example, here is an actor that handles only messages of type Int:
scala> val intActor = actor { receive {
case x: Int => // I only want Ints println("Got an Int: "+ x)
}
}
intActor: scala.actors.Actor = scala.actors.Actor$$anon$1@34ba6b
If you send a String or Double, for example, the intActor will silently ignore the message:
scala> intActor ! "hello" scala> intActor ! math.Pi
But if you pass an Int, you’ll get a response printed out:
scala> intActor ! 12 Got an Int: 12
32.3 Treating native threads as actors
The actor subsystem manages one or more native threads for its own use. So long as you work with an explicit actor that you define, you do not need to think much about how they map to threads.
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index
Section 32.4 |
Chapter 32 · Actors and Concurrency |
730 |
The other direction is also supported: every native thread is also usable as an actor. However, you cannot use Thread.currentThread directly, because it does not have the necessary methods. Instead, you should use Actor.self if you want to view the current thread as an actor.
This facility is especially useful for debugging actors from the interactive shell. Here’s an example:
scala> import scala.actors.Actor._ import scala.actors.Actor._
scala> self ! "hello"
scala> self.receive { case x => x } res6: Any = hello
The receive method returns the value computed by the partial function passed to it. In this case, the partial function returns the message itself, and so the received message ends up being printed out by the interpreter shell.
If you use this technique, it is better to use a variant of receive called receiveWithin. You can then specify a timeout in milliseconds. If you use receive in the interpreter shell, then the receive will block the shell until a message arrives. In the case of self.receive, this could mean waiting forever! Instead, use receiveWithin with some timeout value:
scala> self.receiveWithin(1000) { case x => x } // wait a sec!
res7: Any = TIMEOUT
32.4 Better performance through thread reuse
Actors are implemented on top of normal Java threads. As described so far, in fact, every actor must be given its own thread, so that all the act methods get their turn.
Unfortunately, despite their light-sounding name, threads are not all that cheap in Java. Threads consume enough memory that typical Java virtual machines, which can host millions of objects, can have only thousands of threads. Worse, switching threads often takes hundreds if not thousands of processor cycles. If you want your program be as efficient as possible, then it is important to be sparing with thread creation and switching.
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index
Section 32.4 |
Chapter 32 · Actors and Concurrency |
731 |
To help you conserve threads, Scala provides an alternative to the usual receive method called react. Like receive, react takes a partial function. Unlike receive, however, react does not return after it finds and processes a message. Its result type is Nothing. It evaluates the message handler and that’s it.2
Because the react method does not need to return, the implementation does not need to preserve the call stack of the current thread. Instead, the library can reuse the current thread for the next actor that wakes up. This approach is so effective that if every actor in a program uses react instead of receive, only a single thread is necessary in principle to host all of the program’s actors (to be sure, if your computer has several processor cores, the actors subsystem will use enough threads to utilize all cores when it can).
In practice, programs will need at least a few receive’s, but you should try to use react whenever possible so as to conserve threads.
Because react does not return, the message handler you pass it must now both process that message and arrange to do all of the actor’s remaining work. A common way to do this is to have a top-level work method—such as act itself—that the message handler calls when it finishes. Listing 32.3 shows an example that uses this approach.
The actor shown in Listing 32.3 waits for strings that are host names, and if there is one, returns an IP address for that host name. Here’s an example:
scala> NameResolver.start()
res0: scala.actors.Actor = NameResolver$@90d6c5
scala> NameResolver ! ("www.scala-lang.org", self)
scala> self.receiveWithin(0) { case x => x }
res2: Any = Some(www.scala-lang.org/128.178.154.102)
scala> NameResolver ! ("wwwwww.scala-lang.org", self)
scala> self.receiveWithin(0) { case x => x } res4: Any = None
2Behind the scenes, react will throw an exception after its done.
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index
Section 32.4 |
Chapter 32 · Actors and Concurrency |
732 |
object NameResolver extends Actor {
import java.net.{InetAddress, UnknownHostException}
def act() { react {
case (name: String, actor: Actor) => actor ! getIp(name)
act()
case "EXIT" =>
println("Name resolver exiting.") // quit
case msg =>
println("Unhandled message: "+ msg) act()
}
}
def getIp(name: String): Option[InetAddress] = { try {
Some(InetAddress.getByName(name)) } catch {
case _:UnknownHostException => None
}
}
}
Listing 32.3 · An actor that calls react.
Writing an actor to use react instead of receive is challenging, but pays off in performance. Because react does not return, the calling actor’s call stack can be discarded, freeing up the thread’s resources for a different actor. At the extreme, if all of the actors of a program use react, then they can be implemented on a single thread.
This coding pattern is so common with event-based actors, there is special support in the library for it. The Actor.loop function executes a block of code repeatedly, even if the code calls react. NameResolver’s act method can be rewritten to use loop as shown in Listing 32.4. The one difference in behavior between this act method and that of Listing 32.3 is
Cover · Overview · Contents · Discuss · Suggest · Glossary · Index