11

Do I understand correctly that Liskov Substitution Principle cannot be observed in languages where objects can inspect themselves, like what is usual in duck typed languages?

For example, in Ruby, if a class B inherits from a class A, then for every object x of A, x.class is going to return A, but if x is an object of B, x.class is not going to return A.

Here is a statement of LSP:

Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.

So in Ruby, for example,

class T; end
class S < T; end

violate LSP in this form, as witnessed by the property q(x) = x.class.name == 'T'


Addition. If the answer is "yes" (LSP incompatible with introspection), then my other question would be: is there some modified "weak" form of LSP which can possibly hold for a dynamic language, possibly under some additional conditions and with only special types of properties.


Update. For reference, here is another formulation of LSP that I've found on the web:

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

And another:

If S is a declared subtype of T, objects of type S should behave as objects of type T are expected to behave, if they are treated as objects of type T.

The last one is annotated with:

Note that the LSP is all about expected behaviour of objects. One can only follow the LSP if one is clear about what the expected behaviour of objects is.

This seems to be weaker than the original one, and might be possible to observe, but I would like to see it formalized, in particular explained who decides what the expected behavior is.

Is then LSP not a property of a pair of classes in a programming language, but of a pair of classes together with a given set of properties, satisfied by the ancestor class? Practically, would this mean that to construct a subclass (descendant class) respecting LSP, all possible uses of the ancestor class have to be known? According to LSP, the ancestor class is supposed to be replaceable with any descendant class, right?


Update. I have already accepted the answer, but i would like to add one more concrete example from Ruby to illustrate the question. In Ruby, each class is a module in the sense that Class class is a descendant of Module class. However:

class C; end
C.is_a?(Module) # => true
C.class # => Class
Class.superclass # => Module

module M; end
M.class # => Module

o = Object.new

o.extend(M) # ok
o.extend(C) # => TypeError: wrong argument type Class (expected Module)
gnat
  • 21,442
  • 29
  • 112
  • 288
Alexey
  • 932
  • 7
  • 19
  • 2
    Almost all modern languages provide some degree of introspection, so the question is not really specific to Ruby. – Joachim Sauer Jul 30 '12 at 10:13
  • I understand, i gave Ruby just as an example. I do not know, maybe in some other languages with introspection there are some "weak forms" of LSP, but, if i understood the principle correctly, it is incompatible with introspection. – Alexey Jul 30 '12 at 10:29
  • I've removed "Ruby" from title. – Alexey Jul 30 '12 at 10:42
  • 2
    The short answer is they're compatible. Here's a blog post that I mostly agree with: [Liskov Substitution Principle for Duck Typing](http://blog.objectmentor.com/articles/2008/09/06/the-liskov-substitution-principle-for-duck-typed-languages) – K.Steff Jul 30 '12 at 15:06
  • I saw the blog post but did not understand the conclusion. According to the one-line definition of LSP in Wikipedia and my example, they are not compatible if understood literally. I welcome any alternative definition of "LSP" or "property", that i am not aware of. – Alexey Jul 30 '12 at 15:11
  • 2
    @Alexey Properties in this context are invariants of an object. For example, immutable objects have the property that their values do not change. If you look at good unit tests, they should test exactly for these properties. – K.Steff Jul 30 '12 at 15:23
  • @Alexey, you should remove the **duck-typing** tag, since it isn't relevant. Also, LSP is a property of the system (i.e. of _all_ (T, S) pairs), not of any specific pair. – Andres F. Jul 31 '12 at 23:15
  • @Alexey if you've already read Liskov's paper, then you know your suggested property `property q(x) = x.class.name == 'T'` is neither a _behavioral_ nor a _history_ property, and therefore outside the scope of LSP. In general it's also an anti-pattern to check for a type's name! – Andres F. Jul 31 '12 at 23:19
  • Andres, i think you are wrong in the second part: LSP very well makes sense for a pair of classes and a given type specification of the ancestor class, see Liskov's paper. – Alexey Jul 31 '12 at 23:20
  • I can bet it is a property, i think a behavioral one, but i would have to look through the paper again to answer exactly. It can be given as a *post-condition* for `#class` method. – Alexey Jul 31 '12 at 23:23
  • @Alexey No, what you call `#class` is the type's name. It is mentioned as part of a type's specification in the paper, but since it's neither a "behavior" or a "history" property, it's not relevant for LSP. Since your example is trivial, you can bet it's not an oversight of the paper ;) By this point you should be trying to find a non-trivial example which shows introspection is incompatible with LSP. – Andres F. Jul 31 '12 at 23:25
  • In Ruby, `#class` is a method that returns object's class. So it is a behavior, and a particular example of *introspection*. – Alexey Jul 31 '12 at 23:29
  • Did you notice that i already gave my answer to the question and accepted Telastyn's? – Alexey Jul 31 '12 at 23:30
  • @Alexey Yes, your answer is a restatement of what has been said. In Java there is a getClass() method, so? It's NOT part of the object's behavior. It's a meta-property if you will. And you shouldn't be using it in extendable code anyway. Now, please provide a NON-TRIVIAL example. – Andres F. Jul 31 '12 at 23:31

5 Answers5

30

Here's the actual principle:

Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.

And the excellent wikipedia summary:

It states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substituted for objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.).

And some relevant quotes from the paper:

What is needed is a stronger requirement that constrains the behavior of sub-types: properties that can be proved using the specification of an object’s presumed type should hold even though the object is actually a member of a subtype of that type...

A type specification includes the following information:
- The type’s name;
- A description of the type's value space;
- For each of the type's methods:
--- Its name;
--- Its signature (including signaled exceptions);
--- Its behavior in terms of pre-conditions and post-conditions.

So on to the question:

Do I understand correctly that Liskov Substitution Principle cannot be observed in languages where objects can inspect themselves, like what is usual in duck typed languages?

No.

A.class returns a class.
B.class returns a class.

Since you can make the same call on the more specific type and get a compatible result, LSP holds. The issue is that with dynamic languages, you can still call things on the result expecting them to be there.

But let's consider a statically, structural (duck) typed language. In this case, A.class would return a type with a constraint that it must be A or a subtype of A. This provides the static guarantee that any subtype of A must provide a method T.class whose result is a type that satisfies that constraint.

This provides a stronger assertion that LSP holds in languages that support duck typing, and that any violation of LSP in something like Ruby occurs more due to normal dynamic misuse than a language design incompatibility.

Telastyn
  • 108,850
  • 29
  • 239
  • 365
  • 1
    "Since you can make the same call on the more specific type and get a compatible result, LSP holds". LSP holds if the results are identical, if i understood it correctly. Maybe there can be some "week" form of LSP with respect to given constraints, requiring instead of all properties that only the given constraints be satisfied. In any case, i would appreciate any reference. – Alexey Jul 30 '12 at 13:11
  • @Alexey edited to include what LSP means. If I can use B where I expect A, then LSP holds. I'm curious how you think Ruby's .class possibly violates that. – Telastyn Jul 30 '12 at 13:21
  • Telastyn, thank you for the edit, but can you please give me a reference other than Wikipedia summary, which goes there even before the actual one-line definition? The `.class` in Ruby violates LSP because, if your program contains, for example `fail unless x.class == A`, it will pass for `x` of class `A`, but fail for `x` of class `B`. – Alexey Jul 30 '12 at 13:32
  • 4
    @Alexey - If your program contains `fail unless x.foo == 42` and a subtype returns 0 that's the same thing. This isn't a failure of LSP, it's the normal operation of your program. **Polymorphism isn't a violation of LSP.** – Telastyn Jul 30 '12 at 13:44
  • Telastyn, how is this not a violation of LSP? We are returning in circle to the question of what LSP is and what a "property" is, please give me a reference. I am following the one-line definition from Wikipedia. The property *Q* (*x*) = "this program does not fail on input *x*" is true and provable for all objects of class `A`, but not true, and in particular not provable, for any object of class `B`. So LSP, according the the one-line definition, is not satisfied, if there are no objections that *Q* is a property. – Alexey Jul 30 '12 at 13:51
  • 1
    @Alexey - Sure. Let's assume that it is a property. In that case your code does violate LSP since it does not allow subtypes to have the same semantic behavior. But it isn't particularly special to dynamic or duck typed languages. There's nothing they do in their language design that causes the violation. The code you wrote does. Remember, LSP is a principle of program design (hence the *should* in the definition) rather than a mathematical property of programs. – Telastyn Jul 30 '12 at 13:58
  • Excuse me, Telastyn, but LSP first of all is a property of ancestor-descendant relation in a type hierarchy, which is either *true* or *false* in particular situations under particular interpretations. I am not a specialist, but it seems to me that in some languages, like C++, if you forbid inline Assembler and make some other restrictions on what is allowed in the code, it is possible to inherit classes so that LSP will be satisfied. It looks to my like in dynamic reflective languages it is simply impossible. Before i can view LSP as a design principle, i need to understand what is possible. – Alexey Jul 30 '12 at 14:08
  • let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/4311/discussion-between-telastyn-and-alexey) – Telastyn Jul 30 '12 at 14:27
  • 7
    @Alexey: if you write something that depends on x.class == A, then **it is your code that violates LSP**, not the language. It is possible to write code that violates LSP in almost every programming language. – Andres F. Jul 30 '12 at 17:42
  • @Telastyn Can you expand your answer to include what "property" means in this context . That would make it more perfect . The term q(x) as defined in the LSV is a bit obscured. Does it mean some function q of x ? – Geek Aug 01 '12 at 05:01
  • @Geek, q(x) means a property with free variable x, like, for example, "x is red". – Alexey Aug 24 '12 at 19:18
  • I'm left guessing why "x is not an S" must be unprovable. – reinierpost Jun 26 '15 at 15:46
  • @AndresF., i've noticed i didn't answer. "Code" (in the sense you apparently mean) cannot "violate" or "not violate" the LSP, simply because the LSP says nothing about the "code." (LSP is about a pair of types `S` and `T` -- see the quote in Wikipedia, for example.) – Alexey Mar 23 '18 at 09:15
  • 1
    @Alexey That's splitting hairs. Code is types and types are code. However, it's definitely conceptually wrong to claim a *programming language* violates LSP, which is a property of types/code (i.e. of your *program*). – Andres F. Mar 23 '18 at 14:11
  • @AndresF., please read your previous comment to which i replied (with "code" in quotes). You meant by "code" not type definition, but using them (with `x.class == A`). I also do not see what you are talking about in "it's definitely conceptually wrong to claim a programming language violates LSP" -- who sais that some programming language "violates LSP"? (It looked to me that `class` method violated LSP, making Ruby incompatible with LSP, before i've figured out the essential part about *specifications*). – Alexey Mar 23 '18 at 17:15
  • @Alexey "Complying with (or breaking) LSP" is a property of specific programs, not of programming languages like (Ruby) or features (duck typing). A programming language cannot "break" the LSP; the code you write with it possibly does. The LSP is one of those things the programmer must worry about when writing her programs. The programming language lets you define types and it's up to you to define them so that you don't break the LSP. – Andres F. Mar 23 '18 at 20:14
  • @Alexey Also, no offense, but re-reading all your comments here it seems you tend to have a problem with definitions in the English language and that it's become a barrier to understanding. I'm going to guess English is not your native tongue (nothing wrong there -- it's not mine either!). I suggest you ask a Computer Science professor or equivalent academic who speaks your language so that he/she can explain the definition of LSP to you? – Andres F. Mar 23 '18 at 20:23
  • @AndresF., i've lost the thread of your comments, and the problem surely is not a language barrier. Maybe you are talking not about the LSP that is quoted in Wikipedia and stated in the introduction of Liskov's and Wing's paper, but about some other LSP stated elsewhere. In such case i would appreciate a reference, but my question was about the original version. Otherwise, i may only suggest you to read the LSP that i am referring to (it is not about a program, but about a pair of types), and then look again at what i said in my first comment today. – Alexey Mar 23 '18 at 20:44
  • @Alexey I don't need to read up on LSP because it's not me who's asking the question; I'm perfectly fine with my current understanding of the principle. If language is not a barrier to you, but still you refuse or are unable to understand anything anyone tells you here ("lost track" indeed), may I suggest you contact a CS professor? He/she'll likely be clearer than us, and a face to face explanation will be more helpful to you. – Andres F. Mar 24 '18 at 00:14
  • @AndresF.,i did not suggest you to "read up on LSP'', but to read the (version of?) LSP that is the subject of the discussion (it is quoted in my question), and to read carefully the comments you are replying to. Otherwise probably you should not comment. – Alexey Mar 24 '18 at 08:08
  • @Alexey If you paid attention to my comments, dating back to 2012 when you first asked this question, you'll see I've been addressing -- to little success, it would seem -- the topic you raised. Because of this, and because of your inability to tell "property" (as in class field/attribute) and "property" (as in "an observable quality of an entity or program"), it would seem there is a language barrier here. Again, I suggest you consult a professor who speaks your native language. – Andres F. Mar 24 '18 at 15:34
  • @AndresF. your are talking nonsense ("your inability to tell "property" (as in class field/attribute) and "property" (as in "an observable quality" for example). Other than this final observation, i can only refer you to what i said before. – Alexey Mar 24 '18 at 16:29
  • @alexey - for what it is worth, Andres is right. For all practical purposes, code defines types and types are what LSP cares about. – Telastyn Mar 24 '18 at 16:55
  • @Telastyn, i do not know what LSP "cares about", but it talks about types and objects, and the principle itself is specifically about a pair of types. Code like `i = i + 1` or `x.class == A` cannot by itself violate or not violate the LSP (unless, of course, it appears somewhere in type definition). – Alexey Mar 24 '18 at 16:58
  • @alexey - it absolutely, totally can, because that sort of code (when in a member function shared by both types being compared by LSP) defines the semantic contract (or “useful properties” from the Wikipedia page). – Telastyn Mar 24 '18 at 17:27
  • @Telastyn, i never said that my example code `x.class == A` was a part of a "semantic contract" of the type definition -- it was just ("user side") code, exhibiting a property of `x` (the property holds if the expression returns `true`). It looks to me like you (and Andres) are confusing here the LSP (a general rule about "properly" structuring type hierarchy) with a type specification (a rule, how a given type is intended to be used). – Alexey Mar 24 '18 at 18:25
  • @alexey - we are not. – Telastyn Mar 24 '18 at 18:27
7

In the context of LSP a "property" is something that can be observed on a type (or an object). In particular it talks about a "provable property".

Such a "property" could the existence of a foo() method that has no return value (and follows the contract set in its documentation).

Make sure you don't confuse this term with "property" as in "class is a property of every object in Ruby". Such a "property" can be a "LSP property", but it's not automatically the same!

Now the answer to your questions depends a lot on how stringent you define "property". If you say "the property of class A is that .class will return the type of the object", then B actually does have that property.

If, however, you define the "property" to be ".class returns A", then obviously B does not have that property.

However, the second definition isn't very useful, as you've essentially found a round-about way to declare a constant.

Joachim Sauer
  • 10,956
  • 3
  • 52
  • 45
  • I can only think of one definition of a "property" of a program: for a given input it returns a given value, or, more generally, when used as a block in another program, that other program for a given input will return a given values. With this definition, i do not see what it means that "`.class` will return the type of the object". If it means that `x.class == x.class`, this is not an interesting property. – Alexey Jul 30 '12 at 10:23
  • In fact, i would like to know if there is an alternative way to define "properties" of a program in dynamic languages. As you can ask the program about itself, and maybe even ask to return its source code, you cannot hope to change the program and keep all of its properties. – Alexey Jul 30 '12 at 10:35
  • 1
    @Alexey: I've updated my question with a clarification on what "property" means in the context of LSP. – Joachim Sauer Jul 30 '12 at 11:03
  • @@Joachim, can you please give me a reference to the definition of "property" you are using? I do not understand what it means to be "observed". The LSP requires that the properties common to all objects of a class be also common to all objects of every descendant class. – Alexey Jul 30 '12 at 11:29
  • 2
    @Alexey: looking into the paper I don't find a specific definition or "property". That's probably because the term is used in the general CS sense "something that can be observed/proven about some object". It has **nothing** to do with the other meaing "a field of an object". – Joachim Sauer Jul 30 '12 at 11:47
  • Joachim, sorry, what paper do you mean and what is a "field of an object"? I assumed the "property" was what i said in my first comment, but i am trying to find now a more formal definition. Maybe it has to do with [Hoare logic](http://en.wikipedia.org/wiki/Hoare_logic). I do not understand what "observed" means. Also, not all properties can be proved. – Alexey Jul 30 '12 at 11:54
  • @Alexey: in my opinion the property involving class can only be ".class returns the actual runtime type of the object it is called on". And that is true for `A` **and** `B`, so there's no violation of the LSP. If you *define the property differently*, then yes, subclassing *could* break the LSP. – Joachim Sauer Jul 30 '12 at 12:00
  • Joachim, i do not think that "`.class` returns the actual runtime type of the object it is called on" is a property, please give me a reference if i am wrong. You have not defined the "property", so i cannot define it _differently_. Even if the one you suggest is a property and if it makes sense, if this particular one is not violated, does not mean that LSP is not violated. – Alexey Jul 30 '12 at 12:10
  • 4
    @Alexey: I don't know what more I can tell you. I use the definition of "a property is some [*quality*](https://en.wikipedia.org/wiki/Quality_(philosophy)) or attriute of an object". "color" is a *property* of a physical, visible object. "density" is a *property* of a material. "having a specified method" is a *property* of a class/object. – Joachim Sauer Jul 30 '12 at 12:13
  • Basically, as far as i understand, for the phrase you suggest to be a property, there has to be another way, for example a method `.actual_runtime_type`, which returns the actual runtime type, and i will agree then that "`x.class == x.actual_runtime_type`" is a property. If you allow almost anything to be a property, i do not think it would be possible for LSP to hold in any language. – Alexey Jul 30 '12 at 12:15
  • 4
    @Alexey: I think you're throwing baby with the bathwater: Just because for **some** properties the LSP can't be made to hold doesn't mean that it's useless or "doesn't hold in any language". But that discussion would go to far here. – Joachim Sauer Jul 30 '12 at 12:22
5

As I understand it, there's nothing about introspection that would be incompatible with the LSP. Basically, as long as an object supports the same methods as another, the two should be interchangable. That is, if your code expects an Address object, then it doesn't matter if it's a CustomerAddress or a WarehouseAddress, as long as both provide (e.g.) getStreetAddress(), getCityName(), getRegion() and getPostalCode(). You could certainly create some kind of decorator that takes a different type of object and uses introspection to provide the required methods (e.g., a DestinationAddress class that takes a Shipment object and presents the ship-to address as an Address), but it's not required and certainly wouldn't prevent the LSP from being applied.

TMN
  • 11,313
  • 1
  • 21
  • 31
  • "as long as an object supports the same methods as another, the two should be interchangable" what does it mean "the same"? If they are named the same but behave completely differently, the objects are not interchangeable. In fact, depending of what you mean by "interchangeable", even if the methods are not completely identical (like `A#class` and `B#class` are not identical) the objects might not be interchangeable in certain situations. – Alexey Jul 30 '12 at 13:15
  • 2
    @Alexey: The objects are "the same" if they support the same methods. This means the same name, same number and type of arguments, same return type and same side-effects (as long as they are visible to the calling code). The methods may behave completely differently, but as long as they honor the contract, that's OK. – TMN Jul 30 '12 at 13:27
  • TMN, then what about the contract that `x.class.name == 'A'`? This "side-effect" is visible to the calling code. – Alexey Jul 30 '12 at 13:34
  • 1
    @Alexey: but why would I have such a contract? What **actual use** does that contract fulfill? If I had such a contract I could simply replace every occurance of `x.class.name` with `'A' `, effectively making `x.class.name` *useless*. – Joachim Sauer Jul 30 '12 at 14:09
  • @Alexey: also, that's not the contract of `class`: http://ruby-doc.org/core-1.9.3/Object.html#class-method – Joachim Sauer Jul 30 '12 at 14:11
  • @Alexey: The contract specifies the return *type*, not the return *value*. Read up on [design by contract](http://www.eiffel.com/developers/design_by_contract_in_detail.html) and [side effects](http://en.wikipedia.org/wiki/Side_effect_(computer_science)) and you'll see. – TMN Jul 30 '12 at 14:13
  • Joachim, i was not claiming that this contract has an *actual use*, i've made it up. I was claiming the LSP was not satisfied. Which contract of `class` do you mean in the second comment? – Alexey Jul 30 '12 at 14:14
  • Joachim, thanks for the link, this is more interesting, but i haven't found at first glance a precise definition of a contract. Even if contracts are not allowed to specify the return value, there is no such restriction in the definition of LSP. – Alexey Jul 30 '12 at 14:19
  • 1
    @Alexey: again: just because you can define a contract that can't be fullfilled by extending another class doesn't break LSP. It just means you've build an un-extendable class. If I define a method to "return [if the provided code block ends in finite time](https://en.wikipedia.org/wiki/Halting_problem)" then I also have a contract that can't be fulfilled. It doesn't mean that programming is useless. – Joachim Sauer Jul 30 '12 at 14:34
  • "It just means you've build an un-extendable class" Is `class A; end` unextendable?? This is my class. – Alexey Jul 30 '12 at 14:39
  • 2
    @Alexey trying to determine if `x.class.name == 'A'` is an anti-pattern in duck typing: after all, duck typing comes from "If it quacks and walks like a duck, it's a duck". So if it behaves like `A` and honors the contracts that `A` put forth, it's an `A` instance – K.Steff Jul 30 '12 at 15:02
  • @Alexey: that depends, what is the contract of that class? Depending on what you define its contract to be, it *might* be unextendable or not. If the contract contains "`.class.name` must return `A`", then it is in fact unextendable. – Joachim Sauer Jul 30 '12 at 15:38
  • Joachim, this makes sense, but i would like to see this formalized. However, contracts are not mentioned in the original definition of LSP. I think we are spending too much time discussing a 30-something-word definition. To continue this discussion, somebody needs to either state precisely an alternative definition, or give a reference to it. – Alexey Jul 30 '12 at 16:26
  • 1
    @Alexey You've been presented with clear definitions. An object's class isn't part of its behavior or contract or whatever you want to call it. You are equivocating "property" with "object field, such as x.myField", which as has been pointed out, it is NOT the same. In this context, a property is more like a _mathematical_ property, like type invariants. Furthermore, it's an anti-pattern to check for the exact type if you want duck typing. So, what is your problem with LSP and duck typing, again? ;) – Andres F. Jul 30 '12 at 21:13
  • If it is possible for a valid program to ascertain that an object does not implement some hypothetical function without crashing, then any derived class which, when cast to base, would implements that function while its base class does not would violate the LSP, since code could possibly rely upon the fact that the function in question, if implemented, would be implemented in a way different from what the derived class code actually does. For example, suppose a `GraphicsCanvas` base class doesn't have a `SetOpacity` method, but some drawing code that uses one... – supercat Oct 17 '12 at 01:37
  • ...wants to set full opacity if the `GraphicsCanvas` it's given supports that method; that consumer code may require that if the class object it is given supports `SetOpacity`, a value of 1.0 must be fully opaque. Such a condition is met by a `GraphicsCanvas`; it would not be met by a derived class which included a `SetOpacity` method that used 100.0 to represent full opacity. In languages with introspection/reflection, it's impossible to know what such conditions consumers of a class may expect of it, and thus very difficult for derived classes to add anything without violating LSP. – supercat Oct 17 '12 at 01:41
4

After looking through Barbara Liskov's original paper, I've found how to complete the Wikipedia definition so that LSP indeed could be satisfied in almost any language.

First of all, the word "provable" is important in the definition. It is not explained in the Wikipedia article, and "constraints" are mentioned elsewhere without reference to it.

Here is the first important quote from the paper:

What is needed is a stronger requirement that constrains the behavior of sub-types: properties that can be proved using the specification of an object’s presumed type should hold even though the object is actually a member of a subtype of that type...

And here is the second, explaining what a type specification is:

A type specification includes the following information:

  • The type’s name;
  • A description of the type's value space;
  • For each of the type's methods:
    • Its name;
    • Its signature (including signaled exceptions);
    • Its behavior in terms of pre-conditions and post-conditions.

So, LSP only makes sense with respect to given type specifications, and for an appropriate type specification (for the empty one for example), it can be satisfied in probably any language.

I consider Telastyn's answer to be the closest to what I was looking for because the "constraints" were mentioned explicitly.

Alexey
  • 932
  • 7
  • 19
  • Telastyn, if you could add these quotes to your answer, i would prefer to accept yours than my own. – Alexey Jul 31 '12 at 17:55
  • 2
    the markup isn't quite the same, and I changed a little of the emphesis, but the quotes were included. – Telastyn Jul 31 '12 at 19:24
  • Sorry, Joachim Sauer already mentioned "provable" properties, which you disliked. In general, you've merely restated existing answers. Honestly, I don't know what you're looking for... – Andres F. Jul 31 '12 at 23:36
  • No, it was not explained "provable from what?". The property `x.class.name = 'A'` is provable for all `x` of class `A` if you allow too much knowledge. The *type specification* was not defined, and its exact relation with LSP was not either, though informally some indications were given. I have already found what i was looking for in Liskov's paper and answered my question above. – Alexey Jul 31 '12 at 23:47
  • I think the words you emboldened are the key. If the supertype documents that for any `x` of that type, `x.woozle` will yield `undefined`, then no type for which `x.woozle` doesn't yield `undefined` will be a proper subtype. If the supertype doesn't document anything about `x.woozle`, the fact that using `x.woozle` on the supertype will happen to yield `undefined` would imply nothing about it what might do on the subtype. – supercat Dec 13 '17 at 17:50
3

To quote Wikipedia's article on LSP, "substitutability is a principle in object-oriented programming." It is a principle and part of the design of your program. If you write code that depends on x.class == A, then it is your code that is violating LSP. Note this kind of broken code is also possible in Java, no duck typing necessary.

Nothing in duck typing inherently breaks LSP. Only if you misuse it, as in your example.

Additional thought: doesn't explicitly checking for an object's class defeat the purpose of duck typing, anyway?

Andres F.
  • 5,119
  • 2
  • 29
  • 41
  • Andres, can you give your definition of LSP, please? – Alexey Jul 30 '12 at 18:15
  • 1
    @Alexey The precise definition of LSP is stated in Wikipedia in terms of subtypes. The informal definition is `Liskov's notion of a behavioral subtype defines a notion of substitutability for mutable objects; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).`. The exact class of an object is NOT one of "the desirable properties of the program"; otherwise it would run contrary to not only duck typing but of subtyping in general, Java's flavor included. – Andres F. Jul 30 '12 at 18:34
  • 2
    @Alexey Also note that your example of `x.class == A` violates both LSP **and** duck typing. No sense in using duck typing if you're going to check for the actual types. – Andres F. Jul 30 '12 at 18:35
  • Andres, this definition is not precise enough for me to understand. Which program, a given one, or any? What is a desirable property? If the class is in a library, different applications may consider different properties desirable. A do not see how the line of code could violate LSP, because i thought that LSP was a property of a pair of classes in a given programming language: either (`A`, `B`) satisfy LSP or not. If LSP depends on the code used elsewhere, it is not explained what code is allowed. I hope to find something here: http://www.cse.ohio-state.edu/~neelam/courses/788/lwb.pdf – Alexey Jul 30 '12 at 19:05
  • 2
    @Alexey LSP holds (or doesn't) for a specific design. It is something to look for in a design; it's not a property of a language in general. It doesn't get any more precise than the actual definition: `Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T`. It's obvious that `x.class` is not one of the interesting properties here; otherwise **Java's polymorphism** wouldn't work either. There is **nothing** inherent to duck typing in your "x.class problem". Do you agree so far? – Andres F. Jul 30 '12 at 19:40
  • @Alexey In answer to your question: no, LSP is _not_ a property of a pair (A, B), but of your overall design. LSP pretty much depends on the code, which must be designed such that substitutions by subtypes don't break it. – Andres F. Jul 30 '12 at 19:58
  • "LSP is not a property of a pair (A, B), but of your overall design.", Andres, i am asking for a reference please. According to the definition i saw, it looks like a property of `A,B` in the programming language. By the way, from the practical side, i thought that when i design two classes, i am not supposed to worry or even know about the "overall design". Also, where did you find the word "interesting" in the definition you have just cited? – Alexey Jul 31 '12 at 07:37
  • Sorry, Andres, i haven't answered to you question if i agree. I agree that duck typing is not the direct cause of "my problem", the introspection is ;). I only mentioned duck typing because it is often accompanied by introspection. I agree that instead of the property "`x.class.name == 'A'`", it is more common to deal with properties like "`x.respond_to?(:to_a)`" in duck typed languages, this does not change the fact that almost any introspection makes LSP impossible in the literal sense of the one-line wikipedia definition. – Alexey Jul 31 '12 at 08:13
  • @Alexey I've already given the reference: Wikipedia quotes the succint form of LSP, from the paper. It is clear that it's not a property of (A, B), but of _every_ (A, B), i.e. of the whole design of the system. Sorry to be rude, but by now I think you have a problem understanding mathematical formulations. It's obvious Barbara Liskov **didn't consider the "name of the type" a relevant property**, otherwise her LSP **would never hold**. If we haven't convinced you yet, there's nothing more to do. I suggest you read the paper. – Andres F. Jul 31 '12 at 14:25
  • @Alexey Just checked the paper. Most of the introduction is spent describing what Liskov considers "relevant properties", what she calls "safety properties". Read it and all your questions will be answered :) – Andres F. Jul 31 '12 at 14:59
  • Andres, thanks for a helpful advice and for your opinion. Mathematical formulations can be tricky indeed :). – Alexey Jul 31 '12 at 18:06