The Liskov Substitution Principle state that
if
S
is a subtype ofT
, then objects of typeT
may be replaced with objects of typeS
without 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 pf
without altering my program
. So in my opinion it is a violation of LSP.
What do you think ?
Function1
that 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 aFunction1
can still have inputs that throw exceptions. e.g.BigDecimal("abc")
. The main difference betweenPartialFunction
andFunction1
is that you have a built-in way of checking whether or not elements are defined first. – Michael Zajac