Posted in Functional Programming in Scala

Functional Program Design

 

JSON

Case classes are Scala’s preferred way to define complex data. Below is one way to represent JSON data

Left hand-side of a For-expression generator can also be a pattern as shown below which lists all first-name and last-name of phone#s that start with 212:

Here JObj(bindings) <- data acts as implicit filter

distinct is needed to remove duplicate authors who are in results list more than twice

Partial Functions

Partial Function, as opposed to Total functions, provides an answer only for a subset of possible data, and defines the data it can handle by isDefined as follows:

More common way of writing Partial Functions is using "case" statement which gives a default implementation of isDefined:

Note that our exception changed to MatchError instead of ArithmeticException when using "case".

Partial-Functions are well handled by "collect" as shown below:

Magic here is that collect expects a PartialFunction and invokes the isDefined method. If we define partial-function inline, the compiler knows that it’s a partial function and you avoid explicit PartialFunction trait.

Seq,Set and Map are also Partial-functions

Checking for isDefined can be painful and luckily Scala supports lift method that converts partial-function to total-function which returns an Option

(above notes on Partial functions has been taken from this blog)

We can chain together partial-functions using orElse or andThen which are defined in PartialFunction trait just like lift.

withFilter

Scala provides another variation of filter called withFilter which doesn't create a new collection like filter does. It acts like a view that filters the result to be passed on to subsequent calls to map/flatMap etc.

We can't use filter after applying withFilter but we can apply multiple withFilters

Scala compiler translated for-expression into higher-order functions using map, flatMap and filter. For eg, for (x <- e1) yield e2
is translated to e1.map(x => e2)

Translation of for is not limited to lists or sequences or even collections.
It is based solely on the presence of the methods map, flatMap and withFilter. This lets us use for syntax for our own types as well – you must only define map, flatMap and withFilter for these types.
There are many types for which this is useful: arrays, iterators, databases, XML data, optional values, parsers, etc.

As long as client interface to the database defines the methods map, flatMap and withFilter we can use for syntax database querying.
This is the basis of Scala database connection frameworks ScalaQuery and Slick.
Similar ideas underly Microsoft’s LINQ.

Streams

Streams are similar to Lists except for their Tails are evaluated on demand.

Streams are defined from a constant Stream.empty and a constructor Stream.cons.

They can also be defined like the other collections by using the
object Stream as a factory Stream(1, 2, 3)

The toStream method on a collection will turn the collection into a stream:
(1 to 1000).toStream > res0: Stream[Int] = Stream(1, ?)

Stream supports almost all methods of List except for :: which always produces a List. #:: should be used instead to produce a Stream which can be used in Expressions as well as Patterns.

To find the second prime number between 1000 and 10000:
((1000 to 10000).toStream filter isPrime)(1)

x #:: xs == Stream.cons(x, xs)

Even the implementation of Streams are very close to Lists with only major difference being the use of "Call-by-name" to declare second param of cons ro filter ops as follows which causes lazy-evaluation.