Virtuous Programmer Adventures of an Autodidact

A Month With Scala

What's this all about?
6Dec/100

Scala 1/4: Getting Started

Posted by Frank Berthold

This is the first article in a series, to read the whole series go here.

Scala

"The work on Scala stems from a research effort to develop better language support for component software." -Scala Overview

Scala is:

  • Object Oriented
  • Functional
  • Strongly Typed
  • A Research Language
  • JVM Based

Why Scala is Interesting

The overall theme of Scala seems to be the same as the theme with any building toy: make it possible to create interesting things from relatively simple things. While studying it I've been repeatedly surprised by finding language constructs that are not from the core language, but can be created from it. For example, Erlang is well known for its actor model for concurrency. With Scala it's possible to create such a system in libraries.

Purely Object Oriented... and Functional?

Scala is both object oriented and functional. My first reaction was that this is a contradiction in terms. The minimum requirement for a language to be functional is that all functions are first class objects. While there are a lot of ideas attached to object oriented languages, at the core an object oriented language has two basic attributes:

  1. Every element in the language is an object.
  2. Every action is performed by a method call from one of those objects.

Scala resolves this apparent contradiction by making functions objects in the object oriented sense. Each function is a singleton object with the single method apply which is executed when the function is called.

Powerful Pattern Matching

Most programming languages have some variant on:

(pseudo code)
switch val:
  "first" -> res1
  "second" -> res2

Which is syntactic sugar for:

(pseudo code)
if val == "first":
  res1
elif val == "second":
  res2

Scala's match functionality is a generalization of this pattern that applies to classes as well as values, for example if you first create a couple of case classes:

  1. case class Node(left: Tree, right: Tree) extends Tree
  2. case class Leaf(num: Int) extends Tree
  3.  

You can then create a function that will sum the tree:

  1. def sumTree(tree: Tree): Int = tree match {
  2.   case Node(l, r) => sumTree(l) + sumTree(r)
  3.   case Leaf(n)    => n
  4. }
  5.  

This will look familiar to those who've coded in languages with Algebraic Type systems like Haskell. This is a case of Scala taking two relatively common elements, classes and switch statements, and combining them into something that is more powerful.

Access to Java's Libraries

One of the major problems with any programming language is having sufficient libraries to be useful for real tasks. For a language to have these on its own it must either have a large community to produce the libraries over time or an exceptionally dedicated small community. Scala solves this problem by having transparent access to Java's libraries, and thus the work of Java's very large community. Any Java library can be imported into a Scala program as though it were one of Scala's native libraries. Strictly speaking since Scala compiles to Java bytecode, it is.

Scala Installation

Before you install Scala, you'll most likely want to make sure you have Java's JDK installed. You can pick it up here.

Scala doesn't have a true installer, so you have to do a bit of manual work to get started:

  1. Download the zip file from Scala Downloads.
  2. Unzip the file into your C:\ directory.
  3. Renaming the resulting directory to something like "scala" may make your life easier if you have to upgrade later.
  4. Add "C:\scala\bin" to your path in environment variables.
  5. If you have Java installed and you want to be able to run Scala programs using Java, add "C:\scala\lib*" to your CLASSPATH environment variable.

Hello World

Scala as Script Interpreter

Scala has a double life, it's both a compiler for Java bytecode and a scripting language. When it's acting as a scripting language the rules are a little more flexible. Compiled, Scala's syntax requires that everything be contained in an object or class. When run through its interpreter you can just have sequences of commands. For example:

HelloWorld1.scala:

  1. println("Hello World!")
  2.  

You can run this program from the command line with: scala HelloWorld1.scala

Scala as Compiler

HelloWorld2.scala:

  1. object HelloWorld {
  2.   def main(args: Array[String]) {
  3.     println("Hello World!")
  4.   }
  5. }
  6.  

This version of hello world can be run by first compiling it with scalac HelloWorld2.scala then running it in Java's VM with java HelloWorld or in Scala with scala HelloWorld. As with Java, by default the name of the produced class comes from the main class, or here singleton object.

Scala's Interactive Shell

Interactive shell has to be one of my favorite language feature and Scala's is rather nice. To start it up type scala at your command line. From there, you can get hello world by typing println("Hello World").

A Couple of Nice Features

When you type something into Scala's interactive shell you'll notice that the interaction looks something like this:

scala> 3 + 4
res0: Int = 7

You can then use res0 as a variable. Additionally if you type :power you'll get access to several nice exploratory features of the language, for example, type in 7. then tab to get all the methods that apply to an integer.

Trivia

  • Scala stands for "scalable language".
  • Scala was originally designed to be interoperable with both Java and .Net.
  • The leader of the Scala team, Martin Odersky, also helped develop Java Generics.

References:

Filed under: hello world, scala No Comments
13Dec/100

Scala 2/4: Loops, Decisions and Tests

Posted by Frank Berthold

This is the second article in a series, to read the whole series go here.

This week I've worked on learning Scala's basic syntactic structures and how to write unit tests. To accomplish this I've implemented a solution to the fizzbuzz problem and written a couple of unit tests to demonstrate that it works.

For an overview of the fizzbuzz problem, go to wikipedia's page.

Conditionals, Loops and Other Constructs

Fizzbuzz.scala

Defining Methods

  1. object Fizzbuzz {
  2.   // Returns true if the given number is divisible by 3
  3.   def isfizz(num : Int) : Boolean = { num % 3 == 0 }
  4.  
  5.   // Returns true if the given number is divisible by 5
  6.   def isbuzz(num : Int) : Boolean = { num % 5 == 0 }
  7.  

The method definition syntax is in the format: def <method name>(<arg name> : <arg type>) : <result type> = <body>. You can see here that there is no 'return' statement in Scala. The value of any given block is the last statement executed in that block. This can lead to some confusion if you have very large methods with a lot of complex branching. The obvious solution is not to have very large methods. I generally start breaking up my methods if they start getting to be more than 12 lines long.

Scala is a statically typed language, which means that all of the variable/argument types are set at compile time. Fortunately it also uses Hindley-Milner type inference which will often guess the intended type of an argument/variable. Because of this the above code can be simplified as:

  1. object Fizzbuzz {
  2.   // Returns true if the given number is divisible by 3
  3.   def isfizz(num : Int) = num % 3 == 0
  4.  
  5.   // Returns true if the given number is divisible by 5
  6.   def isbuzz(num : Int) = num % 5 == 0
  7.  

As far as I'm able to tell, unlike in some systems that use Hindley-Milner, types are required when defining the arguments for a method. I don't really view this as a disadvantage, when I'm coding I get hopelessly confused if I don't have the intended type of my variables noted somewhere, there's no reason not to have it in a way that the compiler can catch my mistakes.

You can also see that when there's only one statement in the method there's no need for the curly braces around it. This can lead to significantly cleaner looking code when you just have a one liner.

If Statements and Exploring Your Options

  1.    def fizzbuzzSingle(num : Int) : Option[String] = {
  2.     if(num <= 0) None
  3.     else if(isfizz(num) && isbuzz(num)) Some("fizzbuzz")
  4.     else if(isfizz(num)) Some("fizz")
  5.     else if(isbuzz(num)) Some("buzz")
  6.     else Some(num.toString())
  7.   }
  8.  

If Statements

One of the ways the Scala team tried to make their language more accessible is to keep the syntax similar to Java where they didn't either simplify it or add functionality which needed new syntax. So anyone who is familiar with any of the Algol type programming languages will recognize the format of the conditional statements above.

Algebraic Data Types

What is less common is Scala's use of an algebraic type system. In short this means that there are types that wrap other types. In the above case the Option type can wrap an arbitrary types. I've used it to wrap a string, bit it could just as easily wrap an Int or a Window object. There are a myriad of reasons for using an algebraic type system. Here it allows me to indicate that an input is invalid (values less than 1) without throwing an exception.

When you are using a package that communicates failure through exceptions, it's often hard to know what exceptions to expect. By using the type system, if you know the type signature of your function/method, you can be sure what to expect as output from your function. When an invalid value is handed to a function using the Option type, it returns None. When it receives a valid value then it returns Some(<response>).

Strictly speaking Scala doesn't have Algebraic types, here for example None and Some are subclasses of the abstract class Option. But for practical purposes it's hard to tell the difference.

Comprehensions and Match

  1.    def fizzbuzzList(end : Int) : List[String] = {
  2.     for (num <- List.range(1, end + 1)) yield fizzbuzzSingle(num) match {
  3.       case Some(n) => n
  4.       case None => ""
  5.     }
  6.   }
  7.  

For Comprehensions

For comprehensions are similar to list comprehensions in other languages with the syntax: for <value> <- <list> if <boolean statement> yield <result>. This will go through each of the values in <list> and if the <boolean statement> returns true then yields the given <result>. The if <boolean statement> part is optional as in the above code.

Match/Case Statements

Scala's match/case statements are a generalization of the more common switch statement. The difference between them are that checking isn't just at a value level, but at a type level also. Here I'm using it to distinguish between the Some and None type, then I can extract the variable value in Some(n) and make use of it.

main

  1.    def fizzbuzzString(end : Int) : String = fizzbuzzList(end).mkString(" ")
  2.  
  3.   def main(args : Array[String]) = println(fizzbuzzString(20))
  4. }
  5.  

When a file contains an object with a main method, it will execute that method when it's associated class is called from Java.

Testing in Scala

TestFizzbuzz.scala

There are a number of unit test frameworks available for Scala. The one that comes with the system, SUnit, has been deprecated. While there isn't a replacement in the standard libraries, the most common recommendations are:

  • ScalaTest: A standard unit testing framework.
  • scalacheck: A test generation framework in the same style as Haskell's quickcheck.
  • specs: A Behavior-Driven-Design style of test framework.

For my purposes I chose scalacheck because it is well suited to testing the fizzbuzz problem, and I'm rather fond of quickcheck. In scalacheck you write assertions about the function that is tested instead of writing explicit test cases. Scalacheck then generates random data to verify those assertions.

Installing the Library

To install scalacheck, download the jar file (scalacheck-..-.*.jar) from scalacheck's download page and save it to Scala's lib directory, c:\scala\lib if you're continuing from last week. 

Importing Libraries

  1. import org.scalacheck._
  2. import Fizzbuzz._
  3.  

Import in Scala looks very similar to Import in Java except instead of using *, you use _ to indicate a wildcard.

Subclassing and Local Imports

  1.  
  2. object TestFizzbuzz extends Properties("fizzbuzzSingle") {
  3.   import Prop._
  4.  

Scala's subclassing and trait system is extraordinarily complex, but just subclassing from one other class is simple: object <object name> extends <super class>

If you only need to use an import in context to a single class or object, then you can import it inside that class or object.

A scalacheck Properties

  1. property("fizzbuzzSingle: < 1 is None") = forAll
  2.   { (num: Int) => (num < 1) ==> (Fizzbuzz.fizzbuzzSingle(num) == None) }
  3.  
  4.   property("fizzbuzzSingle: >= 1 is not None") = forAll
  5.   { (num: Int) => (num >= 1) ==> (Fizzbuzz.fizzbuzzSingle(num) != None) }
  6.  
  7.   property("fizzbuzzSingle: (mod 3) and not (mod 5) is Some(\"fizz\")") =
  8.     forAll (Gen.posNum[Int])
  9.       { (num: Int) => ((num % 3 == 0) && (num % 5 != 0)) ==>
  10.         (Fizzbuzz.fizzbuzzSingle(num) == Some("fizz")) }
  11. }
  12.  

Scalacheck has a lot of depth to it, but getting started is simple. A simple template is:

property("<property description>") = forAll { (<argument> : <type>) => (<limiting condition>) ==> (<assertion>) }

Working backwards, the assertion is a boolean statement that should be true so long as the limiting condition is true. The argument/type combination are the values that you want the system to generate randomly. As in a normal function definition, you can have as many argument/type pairs as you like, separated by commas.

My Experience

Overall I've found working out the fizzbuzz problem in Scala to be interesting and, the language didn't spend much time getting in the way. The two major caveates to that are:

  1. The state of flux of the unit test system, it'd be nice if they'd pick one to include in the package.
  2. When the code is compiled, there are an unusually large number of class files because of the anonymous functions that tend to crop up in functional code (14 if you include the test code).

Resources

20Dec/100

Scala 3/4: XML

Posted by Frank Berthold

This is the third article in a series, to read the whole series go here.

Writing an RSS Aggregator with Scala

One of the cooler things about Scala is its XML processing and production features. Scala has several powerful mechanisms for creating DSL's (Domain Specific Languages) which are essentially special purpose languages inside the parent language. The details of how it goes about this are beyond the scope of this article, but I'll show you one way you can enjoy some of the results.

To illustrate how Scala can be used to process and generate XML, I'll be putting together a simple RSS aggregator. It will print to the screen and write an html file with the channel titles and article titles.

Files Used in this Project

Libraries

  1. import scala.xml._
  2. import java.net.URL
  3.  
  • scala.xml: Scala's XML library has a vast number of features which I've only begun to sample.
  • java.net.URL: Java's library for URL's and HTTP connections.

I've mentioned that Java libraries can be called as though they were Scala libraries elsewhere, this is the first time I've actually done it. It's painless as advertised.

Main Functions, Arrays and Command Line Arguments

  1. object RssReader {
  2.   def main(args : Array[String]) : Unit = {
  3.     val rssFeeds = combineFeeds(getRssFeeds(args(0)))
  4.     printFeeds(rssFeeds)
  5.     writeFeeds(rssFeeds)
  6.   }
  7.  

One piece of syntax that is especially confusing in Scala is how it indicates that one type contains another, in this case Array[String]. This syntax looks a lot like what you'll see for array indexing in most other languages. In Scala arrays are treated as functions that receive an integer argument to return their values, eg args(0) returns the first argument of args. This is something of a simplification, but it helps me remember what the syntax is.

Anonymous Functions, Maps and Folds

  1.   def getRssFeeds(fileName : String) : List[(String, Seq[String])] = {
  2.     // Given a config file containing a list of rss feed URL's,
  3.     // returns a list with ('channel name', ['article 1', 'article 2'])
  4.     var baseList : List[(String, Seq[String])] = List()
  5.     getFileLines(fileName).map(extractRss).foldLeft(baseList)
  6.         { (l, r) => l ::: r.toList }
  7.   }
  8.  

Anonymous Functions

The function on line 4, (l, r) => l ::: r.toList is a case of an anonymous function in Scala, I'll break it down: 1. (l, r) =>: The arguments section, if I wanted to be pedantic I could specify their type, but anonymous functions already clutter code and their purpose should usually be pretty obvious. 2. l ::: r.toList: l is an already existing list object which is being prepended to r after r is turned into a list.

Higher-order functions

Scala's for syntax is extraordinarily powerful, but sometimes it's more than you need. The three most common higher-order functions are:

  • map: Apply a function to each element in a sequence.
  • filter: Use a boolean function to filter the elements of a sequence.
  • fold(generally split into foldLeft and foldRight): Use a 2-arity function to combine all of the elements in a sequence into 1 element.

The for syntax combines the functionality of map and filter, so is most useful when you need to do both to a sequence. It does not cover the functionality of fold. Here I'm:
1. Getting a sequence which contains the URL's of several RSS feeds. 2. Mapping a extractRss over them to get the RSS data. 3. Folding the anonymous function (l, r) => l ::: r.toList over them to change them from immutable Seq values to Lists and combine them.

Filter and Some Sugar

  1.   def combineFeeds(feeds : List[(String, Seq[String])])
  2.       : List[(String, Seq[String])] = {
  3.      // Cleanup function, given a list of feeds it combines duplicate channels.
  4.     def combinedFeed(feedName : String) : Seq[String] = {
  5.       feeds.filter(_._1 == feedName).map(_._2).flatten
  6.     }
  7.  
  8.     val feedNames = feeds.map(_._1).distinct
  9.  
  10.     feedNames.map(x => (x, combinedFeed(x)))
  11.   }
  12.  

The filter function in combineFeed makes use of the fact that it is in combineFeeds's namespace and doesn't need to have feeds passed to it. It filters through feeds, keeping those with the same name. It then passes the result to map which extracts the article lists, and flatten combines each list. Generally you would either pass a named function with a single argument to filter and map, or an anonymous function with the format (x) => x == feedName. This pattern comes up so often that Scala allows us to ignore the usual anonymous function syntax and use the underscore as a placeholder for a single variable.

Brackets are Optional in Single Liners

  1.   def getFileLines(fileName : String) : Array[String] =
  2.     // Extracts the URL's from a file and breaks them up into individual strings.
  3.     scala.io.Source.fromFile(fileName).mkString.split("\n")
  4.  

Opening Network Connections and Reading XML

  1.   def extractRss(urlStr : String) : Seq[(String, Seq[String])] = {
  2.     // Given a URL, returns a Seq of RSS data in the form:
  3.     // ("channel", ["article 1", "article 2"])
  4.     val url = new URL(urlStr)
  5.     val conn = url.openConnection
  6.     val xml = XML.load(conn.getInputStream)
  7.  
  8.     for (channel <- xml \\ "channel")
  9.       yield {
  10.         val channelTitle = (channel \ "title").text
  11.         val titles = extractRssTitles(channel)
  12.         (channelTitle, titles)
  13.     }
  14.   }
  15.  

Opening Network Connections

For practical purposes Lines 2-4 are straight Java. They format a URL, open a connection, download the stream and convert it into the XML library's internal format.

Reading the XML

Line 6 is where I start making good on my promise of easy XML processing. xml \\ "channel" descends through the XML tree and extracts every element named "channel", we then use the for expression to take each of those elements and treat them each as XML trees in their own right. The \\ will descend through the entire tree, while \ only examines those nodes that are immediately under the given root.

  1.   def extractRssTitles(channel : Node) : Seq[String] = {
  2.     // Given a channel node from an RSS feed, returns all of the article names
  3.     for (title <- (channel \\ "item") \\ "title") yield title.text
  4.   }
  5.  

In extractRssTitles you can see how \\ can be nested to do more complex dives into an XML document.

Display

To Screen

  1.   def printFeeds(feeds : List[(String, Seq[String])]) : List[Seq[Unit]] = {
  2.     // Given a list of ("channel", ["article 1", "article 2"]), prints
  3.     //  them each to screen.
  4.     for (feed <- feeds) yield {
  5.           println("*** " + feed._1 + " ***")
  6.           for (title <- feed._2) yield println("\t" + title)
  7.     }
  8.   }
  9.  

printFeeds takes a list of the feed objects and displays them on screen. There are a couple of ways to extract data from a tuple. If you're dealing with a complex set of structures, you can use match/case. I knew I'd always be dealing with a 2-tuple, so I chose to use the _# syntax, where _1 will give you the first value in the tuple, etc.

To HTML

  1.   def writeFeeds(feeds : List[(String, Seq[String])]) = {
  2.     // Given a list of ("channel", ["article 1", "article 2"]), generates
  3.     //  and writes an HTML document listing the articles with channel names
  4.     //  as headers.
  5.     val html =
  6.       <html><title>Rss Feeds</title><body>
  7.         {for (feed <- feeds) yield
  8.           <h2>{feed._1}</h2>
  9.           <ul>
  10.             {for (title <- feed._2) yield <li>{title}</li>}
  11.           </ul>}
  12.         </body> </html>
  13.    
  14.     XML.saveFull("rss.html", html, "UTF-8", true, null)
  15.   }
  16. }
  17.  

When you produce XML in most languages you have to choose between using hard to read code that produces guaranteed well formed XML, or generating your XML out of strings that are easy to read but you can't be sure is well formed.

This is what has had me excessively excited about Scala for the past week. It seems that the majority of projects I get involved in sooner or later involve doing some kind of work with XML, and it's usually one of the ugliest parts of the job. Here you can see I have XML freely interspersed with the rest of my code, and was by far the easies part of what I wrote this week. At any time inside the XML, if I need to break back out into Scala I just surround the statement I want to use in curly braces. With it I've been able to clearly produce XML that I can be confident is well formed.

My Experience

Arrays

An unpleasant surprise I got while I was working on this program was that, unlike other sequence types, Arrays in Scala have a simple way to convert to Lists.

Functional Programming

Reading over my own code, I realize that functional style has become a habit. It'll be interesting to see how well I cope when I try to pick up a language where functional style isn't a realistic option. To anyone reading, if there are places in my Scala code where it would be more clear if I used a more object oriented style, please let me know.

XML

One more time. The XML processing capabilities of this language are beautiful. Scala is going to end up being part of my standard arsenal for this reason alone.

Resources:

Filed under: xml, rss, scala No Comments
27Dec/102

Scala 4/4: GUI

Posted by Frank Berthold

Writing GUI Code in Scala

This is the last article in a series, to read the whole series go here.

The Plan

For the last week of Scala I've put together a GUI for week 3's RSS aggregator using the Swing library. Scala's Swing library is a fairly thin wrapper around Java's Swing library. The primary difference between them is that Scala's library takes advantage of its more powerful type system.

What to Expect

When this article is complete:

  • You will have:
    • A front end for the RSS agregator I wrote in week 3.
  • You will know:
    • How to write a basic Swing application in Scala.
Screenshot:

RSS GUI

Files Used in this Project

The Code

Libraries

  1. import RssReader._
  2. import swing._
  3.  

You can use the libraries that you've written in the same way you call any other Java class library. Remember that you must compile RssReader.scala before you try to compile RssGui.scala.

A Top Level Object

  1. object RssGui extends SimpleSwingApplication {
  2.  

The SimpleSwingApplication incorporates all of the basics that you need for a standard windowed application in order to: create, draw and destroy the window when it's closed.

Container for RSS Data

  1.   var rssFeeds = List(("None Loaded", Seq("")))
  2.  

This will hold the data for the RSS feeds. I've chosen to initialize it with "None Loaded" and an empty string because it gives the GUI something to display until a config file is selected and loaded, and it let's Scala know what the rssFeeds's type is.

File Chooser

  1.   var configFileChooser = new FileChooser(new java.io.File("./"))
  2.   var configFileName = new String
  3.  

The configFileChooser is a FileChooser that will open, defaulting to the current working directory. configFileName will hold the value selected, the details for how this will work are in the "Defining Behaviors" section.

Layout

For the most part the code here stands for itself, which is wonderful. Once you understand the basics for creation and layout for one component, they're all the same. As a result I've be described each new idea in detail when I encountered it and let the rest alone.

Channel Panel Construction
Channel Select
  1.   val channelLabel = new Label {
  2.     text = "Select a channel:"
  3.   }
  4.  
  5.   val channelSelect = new ComboBox(rssFeeds(0)._1) { }
  6.  
  7.   val channelSelectPanel = new BoxPanel(Orientation.Vertical) {
  8.     contents += channelLabel
  9.     contents += channelSelect
  10.     border = Swing.EmptyBorder(0, 0, 0, 30)
  11.   }
  12.  

Lines 1-3: Create a new label with "Select a channel:" for its value.
Line 5: Create a ComboBox that is initialized to the first tuple value in the first list value: "None Loaded"
Line 7: Create a new panel that is filled top to bottom.
Line 8-9: Add the Label and ComboBox to the panel.
Line 10: Surround the panel in an empty border with 30 pixels on the right side (to separate it from the RssDisplay).

Rss Display
  1.   val rssDisplay = new ListView(rssFeeds(0)._2)
  2.  

This creates a nearly empty ListView. It has one line with an empty string. Until it's populated with something more complex, it won't display at all.

Combining channelPanel
  1.   val channelPanel = new BoxPanel(Orientation.Horizontal) {
  2.     contents += channelSelectPanel
  3.     contents += rssDisplay
  4.     border = Swing.EmptyBorder(0, 0, 30, 0)
  5.   }
  6.  

This creates a new panel with Orientation.Horizontal so each element will be added left to right. The channelSelectPanel and rssDisplay are added to complete the channelPanel.

Button Panel Construction
  1.   val setConfigFileButton = new Button {
  2.     text = "Set Config File"
  3.   }
  4.  
  5.   val loadRssButton = new Button {
  6.     text = "Load Feeds"
  7.   }
  8.  
  9.   val buttonPanel = new BoxPanel(Orientation.Horizontal) {
  10.     contents += setConfigFileButton
  11.     contents += loadRssButton
  12.   }
  13.  

Create the two buttons and lays them out in a panel.

Top Window
  1.   val topWindow = new BoxPanel(Orientation.Vertical) {
  2.     contents += channelPanel
  3.     contents += buttonPanel
  4.     border = Swing.EmptyBorder(10, 10, 10, 10)
  5.   }
  6.  

Add the two top level panels together and put a border around them to make it neat.

Defining Behaviors

The Last Bit of Layout

  1.   def top = new MainFrame {
  2.     title = "Rss Reader"
  3.  
  4.     // Overall construction
  5.     contents = topWindow
  6.  

This creates the overall window and assigns topWindow as its contents. The title "Rss Reader" will appear in the title bar of the window.

Listening

  1.     listenTo(channelSelect.selection, setConfigFileButton, loadRssButton)
  2.  

Each of the GUI elements that you want to be able to have act as controllers must be listed in listenTo. I've chosen to add them all at once, you can also add them one at a time as in:

  1.    listenTo(channelSelect.selection)
  2.    listenTo(setConfigFileButton)
  3.    listenTo(loadRssButton)
  4.  

To determine when a value has been selected we are listening to channelSelect.selection instead of channelSelect.

Reacting

For each action performed on one of the listenTo'ed elements it will run through each of the listed reactions to see if any of them are appropriate for the action/object combination.

  1.     reactions += {
  2.       case swing.event.SelectionChanged(`channelSelect`) =>
  3.         rssDisplay.listData = rssFeeds(channelSelect.selection.index)._2
  4.         pack
  5.       case swing.event.ButtonClicked(`setConfigFileButton`) =>
  6.         if(configFileChooser.showOpenDialog(channelSelectPanel) ==
  7.             FileChooser.Result.Approve) {
  8.           configFileName = configFileChooser.selectedFile.getAbsolutePath
  9.         }
  10.       case swing.event.ButtonClicked(`loadRssButton`) =>
  11.         if(configFileName != "") {
  12.           rssFeeds = combineFeeds(getRssFeeds(configFileName))
  13.           channelSelect.peer.setModel(ComboBox.newConstantModel(rssFeeds.map(_._1)))
  14.           rssDisplay.listData = rssFeeds(0)._2
  15.           pack
  16.         }
  17.     }
  18.   }
  19. }
  20.  

For readability I've left the entire set intact above, I'll look at them for each case here.

A New Channel is Selected
  1.       case swing.event.SelectionChanged(\`channelSelect\`) =>
  2.         rssDisplay.listData = rssFeeds(channelSelect.selection.index)._2
  3.         pack
  4.  

Line 1: The case looks for the event, SelectionChanged and to which element that event is applied, channelSelect, the backquotes above are important.
Line 2: When the selection changes, set the display to contain the story titles from the rssFeeds value with the same index as the value selected.
Line 3: Once the values have been changed, packs the window so everything fits.

The Config File Button is Clicked
  1.       case swing.event.ButtonClicked(\`setConfigFileButton\`) =>
  2.         if(configFileChooser.showOpenDialog(channelSelectPanel) ==
  3.             FileChooser.Result.Approve) {
  4.           configFileName = configFileChooser.selectedFile.getAbsolutePath
  5.         }
  6.  

Line 2-3: Opens the configFileChooser and checks to make sure that a value has come back.
Line 4: Assigns the absolute path to the selected file to configFileName.

The Load Rss Button is Clicked
  1.       case swing.event.ButtonClicked(\`loadRssButton\`) =>
  2.         if(configFileName != "") {
  3.           rssFeeds = combineFeeds(getRssFeeds(configFileName))
  4.           channelSelect.peer.setModel(ComboBox.newConstantModel(rssFeeds.map(_._1)))
  5.           rssDisplay.listData = rssFeeds(0)._2
  6.           pack
  7.         }
  8.  

Line 2: Verifies that some value has been assigned to configFileName.
Line 3: Uses combineFeeds from RssReader to read in the RSS values.
Line 4: Set's the channelSelect ComboBox to contain the names of each of the RSS feeds.
Line 5. Set's the rssDisplay to contain the titles from teh first feed.

Final Summary

Scala has been an extraordinarily interesting language. The XML package alone makes it worth adding to your toolbox and, as can be seen by how clean the GUI design code is, it's not a one trick pony. The type system, which sadly I haven't been able to go into nearly enough detail here, has many of the nice features you'll find in Haskell. And of course it's great to be able to make use of all of Java's libraries for free.

I do have a couple of small complaints:

  1. The error messages are sometimes a bit obscure, but this is a common flaw in all but the most mature open source projects.
  2. When code compiles, each anonymous function gets its own class. For anyone coding in a functional style, this quickly leads to a deeply cluttered directory.

These complaints are minor though, and the first can certainly be fixed. Overall it's been a pleasure to work with and I look forward to using it for real tasks in the near future.

Coming Up

Next week I'll be starting on Prolog. Prolog is a declarative language that is centered on formal logic.

Resources

Filed under: gui, scala 2 Comments