A simple example of using java builder in a Functional Way!

Problem

Assume you have 2 optional parameters: Maybe(firstName), Maybe(lastName) You want to instantiate (or build an instance of) Name class which is possibly written in Java with these parameters, if they exist (ie; Maybe.Just, or Option.Some).

Ok! Let us try writing it.

  new Name
   .withFirstName(param1.getOrElse(..well I dont know))
   
   
  // Oh! That doesn't work. We could try this 
   val s = new Something
   
   (firstName, lastName) match {
     case (Just(f), Just(l)) => (new Name).withFirstName(f).withLastName(l)
     case (Empty, Just(l)) => (new Name).withLastName(l)
     case (Just(f), Empty) => (new Name).withFirstName(f)
     case (Empty, Empty) => new Name
   }
 

Think of multiple classes similar to Name with multiple (a list of) optional parameters. Now, this is our problem definition. A quick solution would be abstracting out the above function (say f) and fold the list of optional parameters (using f), but that is in fact, discarding the possibility of using a better mechanism provided by Functional Programming paradigm. Below given is one of the solutions. While this may not be the only solution, it is still a cleaner way of solving this problem.

A Solution

State Monad

@   // Assume that Name is not a scala class, and is a Java bean kind of thing ; 
@   // Name.withFirstName("afsal").withLastName("thaj").withBlaBla.withFirstName("changingafsalsname").withLastName("thoughtofchangingitagain")
 
@   case class Name(firstName: String, lastName: String) {
      def setFirstName(newFirstName: String): Name = Name(newFirstName, lastName)
      def setLastName(newLastName: String): Name = Name(firstName, newLastName)
    } 
@   def buildInstance[A, B](a: Maybe[A])(f: B => A => B) : State[B, Unit] = {
      for {
        instance <- State.get[B]
        modifiedInstance <- State.put(a.cata(f(instance)(_), instance))
      } yield modifiedInstance
    } 
    
    
    

@   val initialStateOfName = Name("wrongfirstname", "wronglastname") 

@   (for {
      _ <- buildInstance[String, Name]("afsal".just)(_.setFirstName)
      _ <- buildInstance[String, Name]("thaj".just)(_.setLastName)
    } yield ()).exec(initialStateOfName) 
res44: Id[Name] = Name("afsal", "thaj")

// What if your lastName parameter was Maybe.Empty (Optional `None`)

 (for {
      _ <- buildInstance[String, Name]("afsal".just)(_.setFirstName)
      _ <- buildInstance[String, Name](Maybe.Empty[String])(_.setLastName)
    } yield ()).exec(initialStateOfName) 
res37: Id[Name] = Name("afsal", "wronglastname")


// And a very simple thing to note, our initialState is immutable, it hasn't changed
@ initialStateOfName 
res38: Name = Name("wrongfirstname", "wronglastname")
  
@   // If you know about only firstName, and doesn't know anything about last Name
@   val initialStateOfName = Name("nonafsal", "thaj") 
@   buildInstance[String, Name]("afsal".just)(_.setFirstName).exec(initialStateOfName) 
res35: Id[Name] = Name("nonafsal", "thaj")

I have intentionally avoided the use of State.modify function in scalaz, since it is less expressive at this stage.

If you don’t know State..

You can see some of my code scribblings on State data-type (without scalaz) in the following links. You may read them in its order. The comments in code may give you some idea on what is state data type. That will make you feel comfortable with some notes in scalaz tutorial (Google).