Algebraic Data Types
FILE : SealedDemo
In this workshop we will see how case classes and pattern matching give us new design possibilities.
When we create abstract sealed trait
sealed trait SomeAbstractType
Then we can only define subtypes in the same files which on the one side limits it extension but on the other side gives full information to compiler on what to expect in pattern matching. So for example if forgot to add one case then compiler will generate warning.
This construction is a lot more powerful that standard Java constructor because we can easily define different constructors.
And in case there are no arguments we can easily model this as singleton object.
final case class Implementation1(number: Int) extends SomeAbstractType
final case class OtherImplementation(text: String) extends SomeAbstractType
case object UniqueSubtype extends SomeAbstractType
The fact that compiler can verify if we handled all cases is important in context of function totality - in other words - if for every input parameter there is valid output value. If we miss case then of course we have a potential bug and this would be another definition of a bug
"bug - partial function treated as a total function"
example match {
case Implementation1(number) => println(s"number=$number")
case OtherImplementation(text) => println(s"tekst=$text")
case UniqueSubtype => println("unique") //comment out and explain warning
}
Closed/Closed principle
Because all data variants are defined and sealed within one file then we can easily implement logic dispatch in abstract type. In the following example we can see part of the custom implementation of Option type which is an example of ADT in scala standard library.
sealed abstract class MyOption[A]{
def getOrElse(other:A):A = this match {
case MySome(a) => a
case _ => other
}
}
final case class MySome[A](a:A) extends MyOption[A]
final case class MyNone[A]() extends MyOption[A]
And now it looks like standard subtype polymorphism but we have full control beacuse of sealed hierarchy
val some:MyOption[Int] = MySome(1)
val result1=some.getOrElse(5)
EXERCISES
FILE : SealedTypesExercises
Exercise1 :
Implement HTTP statuses as Algebraic Data Types
- in abstract trait implement method which return code according to implementation
- in companion object implement utility method which return redirection when it is available
Exercise2
Implement Natural numbers as ADT. To do this you only need Zero and Next number so that 1 is Next(Zero) and 2 is Next(Next(Zero))
Exercise3
Implement part of custom Try which must have proper filter implementation. In this exercise you will have to add subtypes yourself.