The Liskov Substitution Principle state that
if
Sis a subtype ofT, then objects of typeTmay be replaced with objects of typeSwithout altering any of the desirable properties of that program.
However, in Scala, there is the PartialFunction that is not applicable/defined in all cases.
trait Function1 {
def apply(x: A): R
}
trait PartialFunction extends Function1 {
def apply(x: A): R
def isDefinedAt(x: A): Boolean
}
If you apply a PartialFunction to an undefined value, you will receive an exception.
A convenient way to create a PartialFunction in scala is to use pattern matching. Doing so, you receive a MatchError for undefined values.
val fn:Function1[String, Int] = s => s.length
val pf:PartialFunction[String, Int] = {
case "one" => 3
}
def program(f:Function1[String, Int], s:String):(Boolean, Int) = (
f.isInstanceOf[Function1[String, Int]], f(s)
)
program(fn, "one") == program(pf, "one")
program(fn, "two") == program(pf, "two")
fn: String => Int = <function1>
pf: PartialFunction[String,Int] = <function1>
program: program[](val f: String => Int,val s: String) => (Boolean, Int)
res0: Boolean = true
scala.MatchError: two (of class java.lang.String)
at scala.PartialFunction$$anon$1.apply(delme.sc:249)
at scala.PartialFunction$$anon$1.apply(delme.sc:247)
at ...
Both fn and pf are subtypes of Function1, but I cannot substitute fn by pfwithout altering my program. So in my opinion it is a violation of LSP.
What do you think ?
Function1that simply throws exceptions for all inputs other than"one". Your argument for violating LSP is that throwing an exception may be an undesirable alteration, but aFunction1can still have inputs that throw exceptions. e.g.BigDecimal("abc"). The main difference betweenPartialFunctionandFunction1is that you have a built-in way of checking whether or not elements are defined first. - Michael Zajac