[Libre-soc-dev] liskov substitution principle and type assertions/annotations

Hendrik Boom hendrik at topoi.pooq.com
Fri Aug 12 17:48:09 BST 2022


On Thu, Aug 11, 2022 at 10:29:48PM -0700, Jacob Lifshay via Libre-soc-dev wrote:
> On Thu, Aug 11, 2022 at 11:51 AM lkcl <luke.leighton at gmail.com> wrote:
> >
> > Couple of things.
> >
> > Definition of LSP I saw, there is no inheritance tree, it is
> > the API that defines the substitution principle. Creating
> > Base classes *at all* and restricting types DIRECTLY and
> > fundamentally interferes with this extremely fundamental
> > principle on which python is fundamentally founded.
> 
> The solution to that is to realize:
> 1. the original statement of LSP is talking about subtypes in
>     programming language theory and there are multiple ways to
>     translate those programming language theory subtypes into Python:
> 
>     1. subclasses, in which case instanceof(v, BaseType) is correct.
>     This is the definition of LSP I gave previously.
> 
>     2. an object's interface, in which case you can do things such
>     as isinstance(v, collections.abc.Iterable) where every object
>     with an __iter__ method counts as a subclass of Iterable, not
>     just those that inherit from Iterable. You can easily define
>     your own Iterable-style type-checking classes using
>     typing.Protocol and @typing.runtime_checkable:
>     https://docs.python.org/3/library/typing.html#typing.Protocol
> 
>     @typing.runtime_checkable
>     class FooBar(typing.Protocol):
>         def foo(self): ...
>         def bar(self, v): ...
> 
>     class MyClass:
>         def foo(self):
>             print("foo")
>         def bar(self, v):
>             return v + 1
> 
>     assert isinstance(MyClass(), FooBar) # works
> 
> 2. some interfaces should not support subtypes at all, e.g.
>     python len() always returns an int no matter how much you
>     like type substitution and think it's the
>     One True Python Way. For interfaces like that,
>     instanceof(v, TheType) is correct (or maybe even type(v) is TheType).
> 
> > Everyone who uses python well (instead of complaining) knows
> > that float may be LSP substituted for int.
> 
> only one way, if it expects a float, you can pass an int. but if it
> expects an int, passing a float usually won't work. e.g. passing an
> int into math.sin works since it expects a float, passing a float into
> bytearray() doesn't work, since it expects an int:
> 
> >>> bytearray(2.0)
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> TypeError: cannot convert 'float' object to bytearray
> >>> bytearray(2)
> bytearray(b'\x00\x00')
> 
> >  You, Jacob, even
> > developed a small function that could be used either with
> > nmigen expressions or python numbers and if you had forced
> > types upon it that would not have been possible.
> 
> No, it would have been perfectly possible, because the type assertions
> that would be used wouldn't be isinstance(v, int), but instead:
> @typing.runtime_checkable
> class Addable(typing.Protocol):
>     # isn't in python's standard library, but should probably be
>     def __add__(self, other): ...
> assert isinstance(v, Addable)
> >
> > Nobody who uses python a lot gives a shit about how far down
> > the stack an Exception occurs. They want as little code in
> > as compact a form written quickly and readable quickly and
> > GENUINELY do not give a f*** about an int being accidentally
> > put as a float and an Exception occurring later rather than
> > earlier.
> 
> The issue is not as much an exception occurring later in a callee, but
> the code giving you a completely indecipherable error, or, worse yet,
> silently wrong results.
> 
> > Good python programmers know to look out for little
> > things like that, fix the problem and move on.
> >
> > To say just because one person made a mistake and a float
> > got into the system therefore EVERY SINGLE F*****G FUNCTION
> > MUST without fail check every goddamn type,
> 
> I didn't say that iirc (or at least I didn't intend to say that). I
> meant that there should be more than zero checks in the entire code
> base.
> 
> > this is complete
> > overkill, a total overreaction,
> 
> true, because you're attacking a strawman. I'm not advocating for
> complete overkill, I'm advocating for more safety checks than walking
> right against the edge of a cliff with no safety rail out in the
> middle of nowhere in the middle of winter in a snowstorm, where, if
> someone fell off, they'd be lucky to be found and buried by next
> spring.
> 
> > that compromises the power,
> > flexibility, compactness and pragmatism of python, and results
> > in vastly more code that 1 has to be written and 2 has to be
> > read.
> >
> > If we truly wanted to piss about with types then we would
> > have started the entire HDL from scratch in c++ or probably
> > bluespec BSV.
> 
> We picked python because it's popular, not because it has poor type
> safety and, partially because of that, allows you to easily call stuff
> where it will mysteriously misbehave.

If you rally wanted a language that's compact and strongly type-checked,
you would have chosen OCaml, not Python or C++.

Almost no OCaml programmers bother with declaring types on all their
functions, but the compiler still manages to check them.

However, it's not popular; most programmers don't even know it exists.

-- hendrik



More information about the Libre-soc-dev mailing list