Saturday, April 29, 2017

Scala Dessign Patterns: Memoization

Introduction

Memoization is mechanism of recording a function results based on its arguments in order to reduce computation in consecutive calls.

Agenda:

  • Implement Memoization using Scala.
  • Implement Memoization using Scalaz.

 

Code: By Scala


class Hasher extends Memoizer {

  val memoMd5 = memo(md5)

  private def md5(input: String) = {
    println(s"Calling md5 for $input")
    new String(Base64.getEncoder.encode(input.getBytes()))
  }
}

trait Memoizer {

  def memo[X, Y](f: X => Y): X => Y = {
    val cache = mutable.Map[X, Y]()
    (x: X) => cache.getOrElseUpdate(x, f(x))
  }
}

object MemoizationExample extends App {


  val hasher = new Hasher

  println(s"Encode for 'hello' is '${hasher.memoMd5("hello")}'")
  println(s"Encode for 'bye' is '${hasher.memoMd5("bye")}'")
  println(s"Encode for 'hello1' is '${hasher.memoMd5("hello1")}'")
  println(s"Encode for 'hello' is '${hasher.memoMd5("hello")}'")
  println(s"Encode for 'bye' is '${hasher.memoMd5("bye")}'")

}

Code: By Scalaz


class Hasher {

  val memoMd5Scalaz: String => String = Memo.mutableHashMapMemo(md5)

  private def md5(input: String) = {
    println(s"Calling md5 for $input")
    new String(Base64.getEncoder.encode(input.getBytes()))
  }
}

object MemoizationScalazExample extends App {

  val hasher = new Hasher

  println(s"Encode for 'hello' is '${hasher.memoMd5Scalaz("hello")}'")
  println(s"Encode for 'bye' is '${hasher.memoMd5Scalaz("bye")}'")
  println(s"Encode for 'hello1' is '${hasher.memoMd5Scalaz("hello1")}'")
  println(s"Encode for 'hello' is '${hasher.memoMd5Scalaz("hello")}'")
  println(s"Encode for 'bye' is '${hasher.memoMd5Scalaz("bye")}'")
}

Download Code from Github Repo

 

References:

Scala Dessign Patterns: Duck Typing

Introduction

If it looks like a duck, swims like a duck and quack like a duck then it is probably a duck.

It is not the type of object matters but the operations that the objects supports. We can use duck typing for requiring even more methods to be available for an object by just expanding the parameter signature.

Caution: Overusing duck typing can negatively affect the code quality and application performance

 

Agenda:

Duck Typing using Scala

 Code:


class SentenceParserSplit {

  def parse(sentence: String): Array[String] = sentence.split("\\s+")
}

class SentenceParserTokenize {

  def parse(sentence: String): Array[String] = {
    val tokenizer = new StringTokenizer(sentence)
    Iterator.continually({
      val hasMore = tokenizer.hasMoreTokens
      if(hasMore) {
        (hasMore, tokenizer.nextToken())
      } else {
        (hasMore, null)
      }
    }).takeWhile(_._1).map(_._2).toArray
  }
}

object WithDuckTyping {

  def printSentenceParser(sentence: String,
                          parser: {def parse(sentence: String): Array[String]}) = {
    parser.parse(sentence).foreach(println)
  }

  def main(args: Array[String]): Unit = {
    val tokenizerParser = new SentenceParserTokenize
    val splitParser = new SentenceParserSplit

    val sentence = "This is the sentence we will be splitting"

    println("Using the tokenizer parser ... ")
    printSentenceParser(sentence, tokenizerParser)

    println("Using the split parser ... ")
    printSentenceParser(sentence, splitParser)
  }
}

Download Code from Github Repo

 

References:

Scala Dessign Patterns: Lazy Evaluation

Introduction

Lazy Evaluation makes sure that an expression is evaluated only once when it is actually needed.

Agenda:

Lazy Evaluation using Scala

Code:



case class Person(name: String)
class PersonService {
  def getFromDatabase: List[Person] = {
    println("Start fetching data .... ")
    Thread.sleep(3000)
    List(Person("Knoldus"), Person("James"), Person("Taara"))
  }

  def withoutLazyEvaluation(persons: => List[Person]) = {
    println(s"Evaluate Without Lazy First Time ${persons}")
    println(s"Evaluate Without Lazy Second Time ${persons}")
  }

  def withLazyEvaluation(persons: => List[Person]) = {
    lazy val personsCopy = persons
    println(s"Evaluate With Lazy First Time ${personsCopy}")
    println(s"Evaluate With Lazy Second Time ${personsCopy}")
  }
}
object LazyEvaluation extends App {

  val service = new PersonService()
  service.withoutLazyEvaluation(service.getFromDatabase)
  service.withLazyEvaluation(service.getFromDatabase)
}

Download Code from Github Repo

 

References:

Scala Dessign Patterns: Type Class Pattern

Introduction

An important principle of good code design is to avoid repetition and it is known as do not repeat yourself (DRY).

Ad Hoc Polymorphism: 

Ad Hoc Polymorphism is utilizing a possibly different implementations based on Types. 

Agenda

  • Implement Type Class Pattern using Scala.
  • Implement Type Class Pattern using Simulacrum.

Code: By Scala


trait Number[T] {

  def plus(t1: T, t2: T): T
  def minus(t1: T, t2: T): T
  def divide(t1: T, t2: Int): T
  def multiply(t1: T, t2: T): T
  def sqrt(t1: T): T
}

object Number {
  implicit object DoubleNumber extends Number[Double] {
    override def plus(t1: Double, t2: Double): Double = t1 + t2
    override def minus(t1: Double, t2: Double): Double = t1 - t2
    override def divide(t1: Double, t2: Int): Double = t1 / t2
    override def multiply(t1: Double, t2: Double): Double = t1 * t2
    override def sqrt(t1: Double): Double = Math.sqrt(t1)
  }

  implicit object IntNumber extends Number[Int] {
    override def plus(t1: Int, t2: Int): Int = t1 + t2 + 10
    override def minus(t1: Int, t2: Int): Int = t1 - t2
    override def divide(t1: Int, t2: Int): Int = t1 / t2
    override def multiply(t1: Int, t2: Int): Int = t1 * t2
    override def sqrt(t1: Int): Int = Math.sqrt(t1).toInt
  }
}


class StatsExample {

  def mean[T: Number] (xs: Vector[T]): T = implicitly[Number[T]]
    .divide (xs.reduce(implicitly[Number[T]].plus(_, _)), xs.size)

  //assume vector is in sorted order
  def median[T: Number] (xs: Vector[T]): T = xs(xs.size / 2)
}

object StatsExample extends App {

  val intVector = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, 25)
  val doubleVector = Vector(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
    20.0, 21.0, 22.0, 23.0, 24.0, 25.0)

  val example = new StatsExample
  println(s"Mean (int) ${example.mean(intVector)}")
  println(s"Median (int) ${example.median(intVector)}")

  println(s"Mean (double) ${example.mean(doubleVector)}")
  println(s"Median (double) ${example.median(doubleVector)}")
}

Code: By Simulacrum


@typeclass trait Number[T] {

  @op("+") def plus(t1: T, t2: T): T
  @op("-") def minus(t1: T, t2: T): T
  @op("/") def divide(t1: T, t2: Int): T
  @op("*") def multiply(t1: T, t2: T): T
  @op("^") def sqrt(t1: T): T
}

object Number {
  implicit object DoubleNumber extends Number[Double] {
    override def plus(t1: Double, t2: Double): Double = t1 + t2
    override def minus(t1: Double, t2: Double): Double = t1 - t2
    override def divide(t1: Double, t2: Int): Double = t1 / t2
    override def multiply(t1: Double, t2: Double): Double = t1 * t2
    override def sqrt(t1: Double): Double = Math.sqrt(t1)
  }

  implicit object IntNumber extends Number[Int] {
    override def plus(t1: Int, t2: Int): Int = t1 + t2
    override def minus(t1: Int, t2: Int): Int = t1 - t2
    override def divide(t1: Int, t2: Int): Int = t1 / t2
    override def multiply(t1: Int, t2: Int): Int = t1 * t2
    override def sqrt(t1: Int): Int = Math.sqrt(t1).toInt
  }
}


class StatsExample {

  import Number.ops._

  def mean[T: Number] (xs: Vector[T]): T = xs.reduce(_ + _) / xs.size

  //assume vector is in sorted order
  def median[T: Number] (xs: Vector[T]): T = xs(xs.size / 2)
}

object StatsExample extends App {


  val intVector = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, 25)
  val doubleVector = Vector(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
    20.0, 21.0, 22.0, 23.0, 24.0, 25.0)

  val example = new StatsExample
  println(s"Mean (int) ${example.mean(intVector)}")
  println(s"Median (int) ${example.median(intVector)}")

  println(s"Mean (double) ${example.mean(doubleVector)}")
  println(s"Median (double) ${example.median(doubleVector)}")
}

Download Code from Github Repo

 

References:

Scala Dessign Patterns: Stackable Traits

Introduction

Provide different implementation for a method of a class later. 

Agenda:

Stackable Traits using Scala.

Code:


trait StringWriter {
  def write(str: String): String
}

class BasicWriter extends StringWriter {
  override def write(str: String) = s"Write the following string '${str}'"
}

trait CapitalizingStringLetter extends StringWriter {
  abstract override def write(str: String) = {
    super.write(str.split("\\s+").map(_.capitalize).mkString(" "))
  }
}

trait LowerCaseStringLetter extends StringWriter {
  abstract override def write(str: String) = super.write(str.toLowerCase)
}

trait UpperCaseStringLetter extends StringWriter {
  abstract override def write(str: String) = super.write(str.toUpperCase)
}

object StackableTraits extends App {

  val writer1 = new BasicWriter with UpperCaseStringLetter with CapitalizingStringLetter
  val writer2 = new BasicWriter with CapitalizingStringLetter with LowerCaseStringLetter
  val writer3 = new BasicWriter with UpperCaseStringLetter with LowerCaseStringLetter with CapitalizingStringLetter
  val writer4 = new BasicWriter with LowerCaseStringLetter with CapitalizingStringLetter with UpperCaseStringLetter

  println(s"Writer 1: ${writer1.write("Ticket to Canada .... ")}")
  println(s"Writer 2: ${writer2.write("Ticket to Canada .... ")}")
  println(s"Writer 3: ${writer3.write("Ticket to Canada .... ")}")
  println(s"Writer 4: ${writer4.write("Ticket to Canada .... ")}")
}

Download Code from Github Repo

 

References:

Scala Design Patterns By Ivan Nikolov

Scala Dessign Patterns: Pimp My Library

Introduction

We need something extra without modifying original library Or we want to add some additional methods in our existing classes without modifying existing code.

Agenda:

Pimp My Library using Scala

Code:


package object pimplib {

  implicit class StringExtension(val string: String) extends AnyVal {
    def isAllUperCase: Boolean = {
      (0 to string.size - 1 ) find {
        case index => !string.charAt(index).isUpper
      }
    }.isEmpty
  }
}
object StringExtensionLib extends App {

  import pimplib._

  var string = "test"
  println(s"${string} is all upper ${string.isAllUperCase}")
  string = "Test"
  println(s"${string} is all upper ${string.isAllUperCase}")
  string = "TESt"
  println(s"${string} is all upper ${string.isAllUperCase}")
  string = "TEST"
  println(s"${string} is all upper ${string.isAllUperCase}")
}

Download Code from Github Repo

 

References:

Scala Design Patterns By Ivan Nikolov

Scala Dessign Patterns: Lens

Introduction

Immutability Pain.

val moveHarmeet = harmeet.copy(
      company = harmeet.company.copy(
        address = harmeet.company.address.copy(
          city = harmeet.company.address.city.copy(
            country = harmeet.company.address.city.country.copy(
              name = "Canada", code = "CA"
            )
          )
        )
      )
    )

Within complex objects, it is difficult to change inner properties. With Lens, we will see how to use our classes if we want to modify some of their properties.

Agenda

  • Implement Lens using Scalaz.
  • Implement Lens using Monocle.

Code: By Scalaz.

package WithLensPatternV1 {

  import scalaz.Lens

  case class Country(code: String, name: String)

  case class City(name: String, country: Country)

  case class Address(number: String, street: String, city: City)

  case class Company(name: String, address: Address)

  case class User(name: String, company: Company, address: Address)

  object WithLensPatternV1 extends App {
    val uk = Country("United Kingdom", "UK")
    val london = City("London", uk)
    val buckinghamPalace = Address("1", "Buckingham Place Road", london)
    val company = Company("Knoldus", buckinghamPalace)

    val canada = Country("Canada", "CA")
    val toronto = City("Toronto", canada)
    val mcMurray = Address("2", "610 McMurray Rd", toronto)
    val harmeet = User("Harmeet Singh", company, mcMurray)

    println("************ Before Move ************")
    println(harmeet)

    println("************ Move to Canada ************")
    val userCompany = Lens.lensu[User, Company](
      (u, company) => u.copy(company = company), _.company
    )

    val userAddress = Lens.lensu[User, Address](
      (u, address) => u.copy(address = address), _.address
    )

    val companyAddress = Lens.lensu[Company, Address](
      (c, address) => c.copy(address = address), _.address
    )

    val addressCity = Lens.lensu[Address, City](
      (a, city) => a.copy(city = city), _.city
    )

    val cityCountry = Lens.lensu[City, Country](
      (c, country) => c.copy(country = country), _.country
    )

    val countryName = Lens.lensu[Country, String](
      (c, name) => c.copy(name = name), _.name
    )

    val countryCode = Lens.lensu[Country, String](
      (c, code) => c.copy(code = code), _.code
    )

    val userCompanyCountryCode = userCompany >=> companyAddress >=> addressCity >=> cityCountry >=> countryCode
    val userCompanyCountryName = userCompany >=> companyAddress >=> addressCity >=> cityCountry >=> countryName

    val moveHarmeetCode = userCompanyCountryCode.set(harmeet, "CA")
    val moveHarmeetName = userCompanyCountryName.set(moveHarmeetCode, "Canada")

    println(moveHarmeetName)
  }

}

 

Code: By Monocle.

package WithLensPatternV2 {

  import monocle.Lens
  import monocle.macros.GenLens

  case class Country(code: String, name: String)

  case class City(name: String, country: Country)

  case class Address(number: String, street: String, city: City)

  case class Company(name: String, address: Address)

  case class User(name: String, company: Company, address: Address)

  object WithLensPatternV2 extends App {

    val uk = Country("United Kingdom", "UK")
    val london = City("London", uk)
    val buckinghamPalace = Address("1", "Buckingham Place Road", london)
    val company = Company("Knoldus", buckinghamPalace)

    val canada = Country("Canada", "CA")
    val toronto = City("Toronto", canada)
    val mcMurray = Address("2", "610 McMurray Rd", toronto)
    val harmeet = User("Harmeet Singh", company, mcMurray)

    println("************ Before Move ************")
    println(harmeet)

    println("************ Move to Canada ************")
    val userCompany: Lens[User, Company] = GenLens[User](_.company)
    val userAddress = GenLens[User](_.address)
    val companyAddress = GenLens[Company](_.address)
    val addressCity = GenLens[Address](_.city)
    val cityCountry = GenLens[City](_.country)
    val countryCode = GenLens[Country](_.code)
    val countryName = GenLens[Country](_.name)

    val userCompanyCountryCode = userCompany composeLens companyAddress composeLens addressCity composeLens cityCountry composeLens countryCode
    val userCompanyCountryName = userCompany composeLens companyAddress composeLens addressCity composeLens cityCountry composeLens countryName

    val moveHarmeetCode = userCompanyCountryCode.set("CA")(harmeet)
    val moveHarmeetName = userCompanyCountryName.set("Canada")(moveHarmeetCode)

    println(moveHarmeetName)
  }

}


Download Code from Github Repo

 

References:

Scala Design Patterns By Ivan Nikolov