Scala and Functional Style: A Practical Example by Venkat Subramaniam
An excerpt from Functional Programming: A PragPub Anthology
✒ Editor’s note: The Pragmatic Bookshelf has a wide selection of books on functional programming topics. You can start by reading Functional Programming: A PragPub Anthology directly on Medium, or browse some of our other offerings.
Functional programming emphasizes immutability, but it’s equally about designing with state transformation and function composition.
In object-oriented programming, we strive for good object composition. In functional programming, we design with function composition. Rather than mutating state, state is transformed as it flows through the sequence of functions.
Let’s construct an example to see what this difference between imperative and functional style looks like in practice. Let’s say we’re given a list of ticker symbols and our goal is to find the highest-priced stock not exceeding $500.
Let’s start with a sample list of ticker symbols.
val tickers = List("AAPL", "AMD", "CSCO", "GOOG", "HPQ", "INTC",
"MSFT", "ORCL", "QCOM", "XRX")
For convenience (and to avoid cryptic symbols in code), let’s create a case class to represent a stock and its price (case classes are useful to create immutable instances in Scala that provide quite a few benefits, especially ease in pattern matching, a common functional style you’ll see explored in depth in Chapter 10, Patterns and Transformations in Elixir).
case class StockPrice(ticker : String, price : Double) {
def print =
println("Top stock is " + ticker + " at price $" + price)
}
Given a ticker symbol, we want to get the latest stock price for that symbol. Thankfully, Yahoo makes this easy.
def getPrice(ticker : String) = {
val url = s"http://download.finance.yahoo.com/d/quotes.csv?s=${ticker}&f=snbaopl1"
val data = io.Source.fromURL(url).mkString
val price = data.split(",")(4).toDouble
StockPrice(ticker, price)
}
We fetch the latest stock price from the Yahoo URL, parse the result, and return an instance of StockPrice
with the ticker symbol and the price value.
To help us pick the highest-priced stock valued not over $500, we need two functions: one to compare two stock prices, and the other to determine if a given stock price is not over $500.
def pickHighPriced(stockPrice1 : StockPrice, stockPrice2 :
StockPrice) =
if(stockPrice1.price > stockPrice2.price) stockPrice1
else stockPrice2
def isNotOver500(stockPrice : StockPrice) = stockPrice.price < 500
Given two StockPrice
instances, the pickHighPriced
function returns the higher priced. The isNotOver500
will return a true
if the price is less than or equal to $500, false
otherwise.
Here’s how we would approach the problem in imperative style:
import scala.collection.mutable.ArrayBuffer
val stockPrices = new ArrayBuffer[StockPrice]
for(ticker <- tickers) {
stockPrices += getPrice(ticker)
}
val stockPricesLessThan500 = new ArrayBuffer[StockPrice]
for(stockPrice <- stockPrices) {
if(isNotOver500(stockPrice)) stockPricesLessThan500 += stockPrice
}
var highestPricedStock = StockPrice("", 0.0)
for(stockPrice <- stockPricesLessThan500) {
highestPricedStock =
pickHighPriced(highestPricedStock, stockPrice)
}
highestPricedStock print
//Top stock is AAPL at price $377.41
Let’s walk through the code to see what we did.
First we create an instance of ArrayBuffer
, which is a mutable collection. We invoke the getPrice()
function for each ticker and populate the stockPrices
ArrayBuffer
with the StockPrice
instances.
Second, we iterate over these stock prices and pick only stocks that are valued less than $500 and add to the stockPricesLessThan500
ArrayBuffer
. This results in possibly fewer elements than we started with.
Finally, we loop through the second collection to pick the stock that is valued the highest among them, again mutating the highestPricedStock
variable as we navigate the collection using the external iterator.
We can improve on this code further, use multiple collections if we desire, wrap the code into separate functions, and put them into a class if we like. However, that will not affect the fundamental approach we took: imperative style with mutable data structure. The state of the collection of stocks and their prices went through quite a few mutations.
Now let’s write this code in functional style. Ready?
tickers map getPrice filter isNotOver500 reduce pickHighPriced print
We’re done. That’s it, small enough to fit in a tweet. OK, this conciseness does take some getting used to. Let’s walk through it.
tickers map getPrice
first transforms the collection of tickers into a collection of StockPrice
instances. For each ticker symbol, we now have the name and price in this collection. The filter function then applies the isNotOver500
on that collection and transforms that into a smaller collection of StockPrices
with only stocks whose prices do not exceed $500. The reduce
function takes that further to pick the highest-priced stock, which we finally hand over to the print
method of StockPrice
.
In addition to being concise, the code does not mutate any state. The state goes through transformations as it flows through the composed functions.
Granted that this functional code is elegant and concise, but what about other considerations, like ease of debugging and performance? These are two concerns I often see raised.
We hope you enjoyed this excerpt. You can continue reading Functional Programming: A PragPub Anthology directly on Medium:
Or purchase the ebook directly from The Pragmatic Bookshelf:
For a print copy, visit bookshop.org.