←back to thread

191 points jwilk | 2 comments | | HN request time: 0s | source
Show context
pansa2 ◴[] No.46230704[source]
I wonder whether Raymond Hettinger has an opinion on this PEP. A long time ago, he wrote: "freezing dicts is a can of worms and not especially useful".

https://mail.python.org/pipermail/python-dev/2006-February/0...

replies(8): >>46230899 #>>46230957 #>>46231064 #>>46231597 #>>46232170 #>>46234698 #>>46235104 #>>46271495 #
zahlman ◴[] No.46231064[source]
> Another PEP 351 world view is that tuples can serve as frozenlists; however, that view represents a Liskov violation (tuples don't support the same methods). This idea resurfaces and has be shot down again every few months.

... Well, yes; it doesn't support the methods for mutation. Thinking of ImmutableFoo as a subclass of Foo is never going to work. And, indeed, `set` and `frozenset` don't have an inheritance relationship.

I normally find Hettinger very insightful so this one is disappointing. But nobody's perfect, and we change over time (and so do the underlying conditions). I've felt like frozendict was missing for a long time, though. And really I think the language would have been better with a more formal concept of immutability (e.g. linking it more explicitly to hashability; having explicit recognition of "cache" attributes, ...), even if it didn't go the immutable-by-default route.

replies(5): >>46231200 #>>46231991 #>>46235637 #>>46236476 #>>46241200 #
pansa2 ◴[] No.46231200[source]
> ImmutableFoo as a subclass of Foo is never going to work. And, indeed, `set` and `frozenset` don't have an inheritance relationship.

Theoretically, could `set` be a subclass of `frozenset` (and `dict` of `frozendict`)? Do other languages take that approach?

> linking [immutability] more explicitly to hashability

AFAIK immutability and hashability are equivalent for the language's "core" types. Would it be possible to enforce that equivalence for user-defined types, given that mutability and the implementation of `__hash__` are entirely controlled by the programmer?

replies(2): >>46231348 #>>46232010 #
chriswarbo ◴[] No.46231348[source]
> Theoretically, could `set` be a subclass of `frozenset` (and `dict` of `frozendict`)?

At one extreme: sure, anything can be made a subclass of anything else, if we wanted to.

At the other extreme: no, since Liskov substitution is an impossibly-high bar to reach; especially in a language that's as dynamic/loose as Python. For example, consider an expression like '"pop" in dir(mySet)'

replies(1): >>46231582 #
tremon ◴[] No.46231582[source]
> consider an expression like '"pop" in dir(mySet)'

  class frozenset:
    pass
  
  class set(frozenset):
    def pop(self, key):
      pass
I don't see why hasattr(mySet, 'pop') should be a problem here?
replies(1): >>46233102 #
chriswarbo ◴[] No.46233102[source]
> I don't see why hasattr(mySet, 'pop') should be a problem here?

I never said it's a problem (and I never said it's not!). I was specifically addressing two things:

- The "theoretical" nature of the question I quoted (i.e. ignoring other aspects like subjectivity, practicality, convention, etc.)

- The reasoning about "Liskov violation", which was quoted further up this thread.

For context, here's Liskov's definition of their principle (from https://en.wikipedia.org/wiki/Liskov_substitution_principle ):

> Barbara Liskov and Jeannette Wing described the principle succinctly in a 1994 paper as follows:[1]

> > Subtype Requirement: Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T.

My expression `"pop" in dir(mySet)` gives an explicit example of how `set` and `frozenset` are not subtypes of each other (regardless of how they're encoded in the language, with "subclasses" or whatever). In this case `ϕ(x)` would be a property like `'"pop" in dir(x)' = 'False'`, which holds for objects x of type frozenset. Yet it does not hold for objects y of type set.

Your example of `hasattr(mySet, 'pop')` gives another property that would be violated.

My point is that avoiding "Liskov violations" is ("theoretically") impossible, especially in Python (which allows programs to introspect/reflect on values, using facilities like 'dir', 'hasattr', etc.).

(FYI I became rather jaded on the Liskov substitution principle after reading https://okmij.org/ftp/Computation/Subtyping )

replies(2): >>46233290 #>>46236254 #
1. tremon ◴[] No.46233290{6}[source]
> > Subtype Requirement: Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T.

This says "if hasattr(parent, 'pop') == True then hasattr(child, 'pop') must be True". This is not violated in this case, since hasattr(parent, 'pop') is False. If you want to extend the above definition so that negative proofs concerning the parent should also hold true for the child, then subtyping becomes impossible since all parent and child types must be identical, by definition.

replies(1): >>46236828 #
2. minitech ◴[] No.46236828[source]
The property in question is `hasattr(x, "pop") is False`.

> If you want to extend the above definition so that negative proofs concerning the parent should also hold true for the child, then subtyping becomes impossible since all parent and child types must be identical, by definition.

The distinction isn’t “negative proofs”, but yes, that’s their point. In Python, you have to draw a line as to which observable properties are eligible.