Scala DuckTyping
Duck Typing is Defined as
In duck typing, a programmer is only concerned with ensuring that objects behave as demanded of them in a given context, rather than ensuring that they are of a specific class
But its best put
“If it looks like a duck and quacks like a duck, it’s a duck”
Its always a good idea to give an example to show (I’m a visual person in that respect) Let say we have a Person Case Class and we know that a person can be married
trait Marriageable
case class Person( firstName: Option[String] = None, lastName: Option[String] = None, married: Option[Boolean] = None ) extends Marriageable Now lets create a MarriageStatus case class
case class MarriedStatus( isMarried : Option[Boolean], marriedDate : Option[String] )
How can we apply a married status to a person? Lets create a generic trait of updatable to which we can wrap MarriedStatus Around We're T is a Marriageable type
trait Updatable[T >: Marriageable] { def applyMarriage[T](updatedObject: T): T }
Lets create a Marriage Wrapper than implements the applyMarriage
case class MarriageHelper(val marriage : MarriedStatus) extends Updatable[Marriageable] { override def applyMarriage[T](updatedObject: T): T = updatedObject match { case person: Person => person.copy( married = marriage.isMarried ).asInstanceOf[T] } }
The results will be apply a marriage object to a Person Let put it all together
package test.ducktyping import org.scalatest.{BeforeAndAfterAll, FlatSpec} class MarriageTest extends FlatSpec with BeforeAndAfterAll { trait Marriageable trait Updatable[T >: Marriageable] { def applyMarriage[T](updatedObject: T): T } case class Person( firstName: Option[String] = None, lastName: Option[String] = None, married: Option[Boolean] = None ) extends Marriageable case class MarriedStatus( isMarried : Option[Boolean], marriedDate : Option[String] ) case class MarriageHelper(val marriage : MarriedStatus) extends Updatable[Marriageable] { override def applyMarriage[T](updatedObject: T): T = updatedObject match { case person: Person => person.copy( married = marriage.isMarried ).asInstanceOf[T] } } behavior of "a marriageable object" it should "be able to apply a marriage to it" in { var husband = new Person(Some("John"), Some("Doe"), Some(false)) var wife = new Person(Some("Jane"), Some("Doe"), Some(false)) val justMarried = new MarriageHelper(new MarriedStatus(Some(true), Some("now"))) println("Husbands Married status = "+husband.married.get) println("Wifes Married status = "+wife.married.get) println("Applying Marriage") husband = justMarried.applyMarriage(husband) wife = justMarried.applyMarriage(wife) println("Husbands Married status = "+husband.married.get) println("Wifes Married status = "+wife.married.get) } }
and it should output Husbands Married status = false Wifes Married status = false Applying Marriage Husbands Married status = true Wifes Married status = true So now we can apply a marriage to any object that is marriageable For eaxmple, we want to marry a dog
case class Dog( name: Option[String] = None, married: Option[Boolean] = None ) extends Marriageable
Then we just update our applyMarriage to tell it how to handle a dog
case dog: Dog => dog.copy( married = marriage.isMarried ).asInstanceOf[T]