• an oop question

    From Julieta Shem@21:1/5 to Then I on Sun Oct 30 11:01:15 2022
    I have a question about a particular case I'm working on. I'm studying
    OOP. To ask the question, I'm going to have to introduce you my context
    here, so you'll need to bear with me. If you'd like to see the question
    right away, go to the section ``My difficulty in encapsulating a
    union''.

    (*) Introduction

    I wrote the classes

    class Empty:
    ...
    class Pair:
    ...

    With them, I can (sort of) make a union of them and build a Lisp-like
    list by defining that my Lisp-like list is either Empty or a Pair. For instance, here's a Lisp-like sequence that represents the string "abc".

    Pair.fromIterable("abc")
    Pair('a', Pair('b', Pair('c', Empty())))

    So far so good. (``Full'' code at the end of this message, if you'd
    like to more carefully look into my reasoning there.)

    (*) How to build a stack?

    These Lisp-like sequences are clearly a stack. You pop an element from
    it by grabbing the first element. You push an element on to it by just
    pairing the new element with the current stack. For instance, let's pop
    the 1-string "a" off of the stack.

    ls = Pair.fromIterable("abc")
    ls.first
    'a'
    ls = ls.rest
    ls
    Pair('b', Pair('c', Empty()))

    Done. Let's push it back on.

    ls = Pair("a", ls)
    ls
    Pair('a', Pair('b', Pair('c', Empty())))

    So far so good, but when it comes to building a better user interface
    for it I have no idea how to do it. I think one of the purposes of OOP
    is to organize code hierarchically so that we can reuse what we wrote.
    So I tried to make a Stack by inheriting Pair.

    class Stack(Pair):
    pass

    Stack(1, Empty())
    Stack(1, Empty())

    Then I wrote pop and push.

    Stack(1, Empty()).pop()
    1

    Stack(1, Empty()).push(2)
    Stack(2, Stack(1, Empty()))

    So far so good. Now let me show you what I can't do.

    (*) The difficulty of encapsulating a union

    The Lisp-like sequences we're building here are union-like data
    structures. A /sequence/ is either Empty() or Pair(..., /sequence/). I
    have not found a way to represent this either-or datastructure with a
    class. For example, there is no way right now to build an empty Stack
    by invoking the Stack constructor.

    Stack()
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: Pair.__init__() missing 2 required positional arguments: 'first' and 'rest'

    As I designed, an empty Stack is represented by Empty(), which is a
    whole other object. Users will have to know this. I wish I did not
    have to burden my users with such knowledge. For instance, I wish the
    answer was "Stack()" to the question of -- ``how can I build an empty
    stack?''

    My desire seems to imply that I need a union-like data structure. If
    Stack is invoked with no arguments, it should produce Empty(), otherwise
    it produces Pair() as it does today.

    How can I achieve that?

    (*) Code

    class Pair:
    def __init__(self, first, rest):
    if not isinstance(rest, Pair) and not isinstance(rest, Empty):
    raise ValueError("rest must be Empty or Pair")
    self.first = first
    self.rest = rest
    def fromIterable(it):
    if len(it) == 0:
    return Empty()
    else:
    return Pair(it[0], Pair.fromIterable(it[1:]))
    def __str__(self):
    return "{}({!r}, {})".format(self.__class__.__name__, self.first, str(self.rest))
    def __repr__(self):
    return str(self)
    def __len__(self):
    return 1 + self.rest.__len__()

    class Empty:
    def __len__(self):
    return 0
    def __str__(self):
    return "Empty()"
    def __repr__(self):
    return self.__str__()
    def __new__(clss):
    if not hasattr(clss, "saved"):
    clss.saved = super().__new__(clss)
    return clss.saved

    class Stack(Pair):
    def pop(self):
    return self.first
    def push(self, x):
    return Stack(x, self)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Sun Oct 30 11:54:24 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:
    My desire seems to imply that I need a union-like data structure.

    You only need to worry about such things in languages with
    static typing. For example, to have a function that can
    sometimes return an int value and at other times a string
    value in C, one would need a union.

    In Python with its dynamic typing, one does not need unions.

    def an_int_sometimes_and_sometimes_a_string( x ):
    if x:
    return 2
    else:
    return "two"

    Nice. This means that I can solve my stack-union problem by writing a procedure --- say stack(...) --- that sometimes gives me Empty() and
    sometimes gives me Stack().

    stack()
    Empty()

    stack(1,2,3,4)
    Stack(4, Stack(3, Stack(2, Stack(1, Empty()))))

    The user interface of this non-empty case is that we're stacking up the arguments, hence the number 1 ends up at the bottom of the stack.

    def stack(*args):
    if len(args) == 0:
    return Empty()
    else:
    return Stack(args[-1], stack(*args[:-1]))

    I realize now that I'm using the same solution of the /open()/
    procedure. Depending on the arguments, open() produces a different type
    of object.

    . If you should, however, be talking about the new "type hints":
    These are static and have "Union", for example, "Union[int, str]"
    or "int | str".

    I ended up locating such features of the language in the documentation,
    but I actually am not interested in declaring the type to the compiler
    (or to the reader).

    I was looking for a solution like yours --- thank you! ---, although I
    was hoping for handling that situation in the construction of the Stack
    object, which was probably why I did not find a way out. Right now I'm
    looking into __new__() to see if it can somehow produce one type or
    another type of object depending on how the user has invoked the class
    object.

    Terminology. By ``invoking the class object'' I mean expressions such
    as Class1() or Class2(). ``Class1'' represents the object that
    represents the class 1. Since the syntax is that of procedure
    invokation, I say ``invoking the class object''.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Sun Oct 30 14:31:01 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    My desire seems to imply that I need a union-like data structure.

    You only need to worry about such things in languages with
    static typing. For example, to have a function that can
    sometimes return an int value and at other times a string
    value in C, one would need a union.

    In Python with its dynamic typing, one does not need unions.

    def an_int_sometimes_and_sometimes_a_string( x ):
    if x:
    return 2
    else:
    return "two"

    a_name_for_an_int_sometimes_and_sometimes_string = 2 a_name_for_an_int_sometimes_and_sometimes_string = "abc"

    . If you should, however, be talking about the new "type hints":
    These are static and have "Union", for example, "Union[int, str]"
    or "int | str".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Julieta Shem on Sun Oct 30 14:30:08 2022
    Julieta Shem <jshem@yaxenu.org> writes:

    [...]

    . If you should, however, be talking about the new "type hints":
    These are static and have "Union", for example, "Union[int, str]"
    or "int | str".

    I ended up locating such features of the language in the documentation,
    but I actually am not interested in declaring the type to the compiler
    (or to the reader).

    I was looking for a solution like yours --- thank you! ---, although I
    was hoping for handling that situation in the construction of the Stack object, which was probably why I did not find a way out. Right now I'm looking into __new__() to see if it can somehow produce one type or
    another type of object depending on how the user has invoked the class object.

    Terminology. By ``invoking the class object'' I mean expressions such
    as Class1() or Class2(). ``Class1'' represents the object that
    represents the class 1. Since the syntax is that of procedure
    invokation, I say ``invoking the class object''.

    An experiment. What's my definition of Stack? It's either Empty or
    Pair, so it's a union. So let us create two inner classes (that is,
    inner to Stack) and make them behave just like Empty and Pair. Using __new__(), we can produce a Stack object that is sometimes Empty() and sometimes Pair(...).

    class Stack:
    class StackEmpty(Empty):
    pass
    class StackPair(Pair):
    pass
    def __new__(clss, *args):
    if len(args) == 0:
    return Stack.StackEmpty()
    else:
    return Stack.StackPair(*args)

    This does it. However, we are exposing the different types to the user.

    Stack()
    Empty()

    Stack(1, Stack())
    Pair(1, Empty())

    Since we have our own copies of Empty and Pair inside Stack, we change
    their representation.

    class Stack:
    class StackEmpty(Empty):
    def __str__(self):
    return "Stack()"
    def __repr__(self):
    return str(self)
    class StackPair(Pair):
    def __str__(self):
    return "Stack({!r}, {})".format(self.first, str(self.rest))
    def __repr__(self):
    return str(self)
    [...]

    Stack()
    Stack()

    Stack(1, Stack())
    Stack(1, Stack())

    That's it. That's what I was looking for. However, I don't really like
    the OOP approach here because of what's going to happen next.

    Now we are free to move on with implementing push and pop, say. But
    since Stack is really a union, we need to define push and pop on each of
    these inner types that make up Stack. Let's do it and see what we get.

    class StackEmpty(Empty):
    [...]
    def pop(self):
    raise ValueError("cannot pop from an empty stack")
    def push(self, x):
    return Stack(x, self)

    class StackPair(Pair):
    def pop(self):
    return self.first
    def push(self, x):
    return Stack(x, self)

    The job is done.

    Stack().push("it")
    Stack('it', Stack())

    Stack(1, Stack()).push(2).push(3)
    Stack(3, Stack(2, Stack(1, Stack())))

    We may observe that we had to write the same exact procedure /push/ in
    both types. Sounds funny to say that it seems that we are too modular
    here. I'm pretty sure you'll find a way to grab the procedure of one
    class and apply it on the other, so I really think there's a way out of
    not wasting keyboard-typing work and keeping things with a ``single
    point of control''.

    (*) Complete code

    class Stack:
    class StackEmpty(Empty):
    def __str__(self):
    return "Stack()"
    def __repr__(self):
    return str(self)
    def pop(self):
    raise ValueError("cannot pop from an empty stack")
    def push(self, x):
    return Stack(x, self)
    class StackPair(Pair):
    def __str__(self):
    return "Stack({!r}, {})".format(self.first, str(self.rest))
    def __repr__(self):
    return str(self)
    def pop(self):
    return self.first
    def push(self, x):
    return Stack(x, self)
    def __new__(clss, *args):
    if len(args) == 0:
    return Stack.StackEmpty()
    else:
    return Stack.StackPair(*args)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Giorgio Pastore@21:1/5 to All on Sun Oct 30 21:06:00 2022
    Il 30/10/22 15:54, Julieta Shem ha scritto:
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:
    My desire seems to imply that I need a union-like data structure.

    You only need to worry about such things in languages with
    static typing. For example, to have a function that can
    sometimes return an int value and at other times a string
    value in C, one would need a union.

    In Python with its dynamic typing, one does not need unions.

    def an_int_sometimes_and_sometimes_a_string( x ):
    if x:
    return 2
    else:
    return "two"

    Nice. This means that I can solve my stack-union problem by writing a procedure --- say stack(...) --- that sometimes gives me Empty() and sometimes gives me Stack().

    I think that Stefan Ram's suggestion provides a solution to the problem
    of the stack, but not in a real OOP flavor.

    You may find useful to learn about the possibilities of using Python
    tools to implement a stack data structure and its manipulation methods.
    A good introduction is at
    https://realpython.com/how-to-implement-python-stack/

    Giorgio

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Giorgio Pastore on Sun Oct 30 20:22:28 2022
    Giorgio Pastore <pastgio@units.it> writes:
    You may find useful to learn about the possibilities of using Python
    tools to implement a stack data structure and its manipulation methods.
    A good introduction is at >https://realpython.com/how-to-implement-python-stack/

    I can't see how lists are not stacks.

    s=[]
    s.append(1)
    s.append(2)
    s.pop()
    |2
    s.pop()
    |1
    s.pop()
    |Traceback (most recent call last):
    | File "<stdin>", line 1, in <module>
    |IndexError: pop from empty list

    So, for practical purposes, there is no need to implement a
    stack class as far as I can understand it.

    Maybe the OP wanted to implement a stack in a special way,
    so that this stack is immutable and its implementation is
    based on something like dotted pairs.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Julieta Shem on Mon Oct 31 09:31:01 2022
    On Mon, 31 Oct 2022 at 09:05, Julieta Shem <jshem@yaxenu.org> wrote:

    Julieta Shem <jshem@yaxenu.org> writes:

    [...]

    . If you should, however, be talking about the new "type hints":
    These are static and have "Union", for example, "Union[int, str]"
    or "int | str".

    I ended up locating such features of the language in the documentation,
    but I actually am not interested in declaring the type to the compiler
    (or to the reader).

    I was looking for a solution like yours --- thank you! ---, although I
    was hoping for handling that situation in the construction of the Stack object, which was probably why I did not find a way out. Right now I'm looking into __new__() to see if it can somehow produce one type or
    another type of object depending on how the user has invoked the class object.

    Terminology. By ``invoking the class object'' I mean expressions such
    as Class1() or Class2(). ``Class1'' represents the object that
    represents the class 1. Since the syntax is that of procedure
    invokation, I say ``invoking the class object''.

    An experiment. What's my definition of Stack? It's either Empty or
    Pair, so it's a union. So let us create two inner classes (that is,
    inner to Stack) and make them behave just like Empty and Pair. Using __new__(), we can produce a Stack object that is sometimes Empty() and sometimes Pair(...).


    The most straight-forward way to represent this concept in an
    object-oriented way is subclassing.

    class Stack:
    ... # put whatever code is common here

    class Empty(Stack):
    ... # put Empty-specific code here, possibly overriding Stack methods

    class Pair(Stack):
    ... # ditto, overriding or augmenting as needed

    This way, everything is an instance of Stack, but they are still
    distinct types for when you need to distinguish.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Weatherby,Gerard@21:1/5 to Then I on Mon Oct 31 00:03:27 2022
    I don’t understand your implementation enough to comment specifically. What’s the definition of Pair? (i.e. what methods and public attributes does it have?) (I manage to escape Lisp as an undergrad)

    To answer your question generally, Union’s are not OO. If you want Pair and Stack to have the same interface create an abstract base class and derive both of them from it.

    https://docs.python.org/3/library/abc.html


    From: Python-list <python-list-bounces+gweatherby=uchc.edu@python.org> on behalf of Julieta Shem <jshem@yaxenu.org>
    Date: Sunday, October 30, 2022 at 5:51 PM
    To: python-list@python.org <python-list@python.org>
    Subject: an oop question
    *** Attention: This is an external email. Use caution responding, opening attachments or clicking on links. ***

    I have a question about a particular case I'm working on. I'm studying
    OOP. To ask the question, I'm going to have to introduce you my context
    here, so you'll need to bear with me. If you'd like to see the question
    right away, go to the section ``My difficulty in encapsulating a
    union''.

    (*) Introduction

    I wrote the classes

    class Empty:
    ...
    class Pair:
    ...

    With them, I can (sort of) make a union of them and build a Lisp-like
    list by defining that my Lisp-like list is either Empty or a Pair. For instance, here's a Lisp-like sequence that represents the string "abc".

    Pair.fromIterable("abc")
    Pair('a', Pair('b', Pair('c', Empty())))

    So far so good. (``Full'' code at the end of this message, if you'd
    like to more carefully look into my reasoning there.)

    (*) How to build a stack?

    These Lisp-like sequences are clearly a stack. You pop an element from
    it by grabbing the first element. You push an element on to it by just
    pairing the new element with the current stack. For instance, let's pop
    the 1-string "a" off of the stack.

    ls = Pair.fromIterable("abc")
    ls.first
    'a'
    ls = ls.rest
    ls
    Pair('b', Pair('c', Empty()))

    Done. Let's push it back on.

    ls = Pair("a", ls)
    ls
    Pair('a', Pair('b', Pair('c', Empty())))

    So far so good, but when it comes to building a better user interface
    for it I have no idea how to do it. I think one of the purposes of OOP
    is to organize code hierarchically so that we can reuse what we wrote.
    So I tried to make a Stack by inheriting Pair.

    class Stack(Pair):
    pass

    Stack(1, Empty())
    Stack(1, Empty())

    Then I wrote pop and push.

    Stack(1, Empty()).pop()
    1

    Stack(1, Empty()).push(2)
    Stack(2, Stack(1, Empty()))

    So far so good. Now let me show you what I can't do.

    (*) The difficulty of encapsulating a union

    The Lisp-like sequences we're building here are union-like data
    structures. A /sequence/ is either Empty() or Pair(..., /sequence/). I
    have not found a way to represent this either-or datastructure with a
    class. For example, there is no way right now to build an empty Stack
    by invoking the Stack constructor.

    Stack()
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: Pair.__init__() missing 2 required positional arguments: 'first' and 'rest'

    As I designed, an empty Stack is represented by Empty(), which is a
    whole other object. Users will have to know this. I wish I did not
    have to burden my users with such knowledge. For instance, I wish the
    answer was "Stack()" to the question of -- ``how can I build an empty
    stack?''

    My desire seems to imply that I need a union-like data structure. If
    Stack is invoked with no arguments, it should produce Empty(), otherwise
    it produces Pair() as it does today.

    How can I achieve that?

    (*) Code

    class Pair:
    def __init__(self, first, rest):
    if not isinstance(rest, Pair) and not isinstance(rest, Empty):
    raise ValueError("rest must be Empty or Pair")
    self.first = first
    self.rest = rest
    def fromIterable(it):
    if len(it) == 0:
    return Empty()
    else:
    return Pair(it[0], Pair.fromIterable(it[1:]))
    def __str__(self):
    return "{}({!r}, {})".format(self.__class__.__name__, self.first, str(self.rest))
    def __repr__(self):
    return str(self)
    def __len__(self):
    return 1 + self.rest.__len__()

    class Empty:
    def __len__(self):
    return 0
    def __str__(self):
    return "Empty()"
    def __repr__(self):
    return self.__str__()
    def __new__(clss):
    if not hasattr(clss, "saved"):
    clss.saved = super().__new__(clss)
    return clss.saved

    class Stack(Pair):
    def pop(self):
    return self.first
    def push(self, x):
    return Stack(x, self)
    -- https://urldefense.com/v3/__https://mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!iK-T12707FwUmb1Qqxlxawj0i9z1tR9aCPWPoHzh5veCzlaFBY9pKfFZwGqg3sDjUqLPC2IZgGw4QI3sRg$<https://urldefense.com/v3/__https:/mail.python.org/mailman/listinfo/python-
    list__;!!Cn_UX_p3!iK-T12707FwUmb1Qqxlxawj0i9z1tR9aCPWPoHzh5veCzlaFBY9pKfFZwGqg3sDjUqLPC2IZgGw4QI3sRg$>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Chris Angelico on Sun Oct 30 21:38:51 2022
    Chris Angelico <rosuav@gmail.com> writes:

    On Mon, 31 Oct 2022 at 09:05, Julieta Shem <jshem@yaxenu.org> wrote:

    Julieta Shem <jshem@yaxenu.org> writes:

    [...]

    . If you should, however, be talking about the new "type hints":
    These are static and have "Union", for example, "Union[int, str]"
    or "int | str".

    I ended up locating such features of the language in the documentation,
    but I actually am not interested in declaring the type to the compiler
    (or to the reader).

    I was looking for a solution like yours --- thank you! ---, although I
    was hoping for handling that situation in the construction of the Stack
    object, which was probably why I did not find a way out. Right now I'm
    looking into __new__() to see if it can somehow produce one type or
    another type of object depending on how the user has invoked the class
    object.

    Terminology. By ``invoking the class object'' I mean expressions such
    as Class1() or Class2(). ``Class1'' represents the object that
    represents the class 1. Since the syntax is that of procedure
    invokation, I say ``invoking the class object''.

    An experiment. What's my definition of Stack? It's either Empty or
    Pair, so it's a union. So let us create two inner classes (that is,
    inner to Stack) and make them behave just like Empty and Pair. Using
    __new__(), we can produce a Stack object that is sometimes Empty() and
    sometimes Pair(...).


    The most straight-forward way to represent this concept in an
    object-oriented way is subclassing.

    class Stack:
    ... # put whatever code is common here

    class Empty(Stack):
    ... # put Empty-specific code here, possibly overriding Stack methods

    class Pair(Stack):
    ... # ditto, overriding or augmenting as needed

    This way, everything is an instance of Stack, but they are still
    distinct types for when you need to distinguish.

    Can you provide a small example? I can't see what you mean, but it
    seems interesting.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to Stefan Ram on Mon Oct 31 13:58:28 2022
    On 31/10/2022 09.22, Stefan Ram wrote:
    Giorgio Pastore <pastgio@units.it> writes:
    You may find useful to learn about the possibilities of using Python
    tools to implement a stack data structure and its manipulation methods.
    A good introduction is at
    https://realpython.com/how-to-implement-python-stack/

    I can't see how lists are not stacks.

    Agreed - in terms of functionality(!)

    s=[]
    s.append(1)
    s.append(2)
    s.pop()
    |2
    s.pop()
    |1
    s.pop()
    |Traceback (most recent call last):
    | File "<stdin>", line 1, in <module>
    |IndexError: pop from empty list

    So, for practical purposes, there is no need to implement a
    stack class as far as I can understand it.

    Maybe the OP wanted to implement a stack in a special way,
    so that this stack is immutable and its implementation is
    based on something like dotted pairs.


    Code is for humans to read too. Accordingly, instead of "s", preference
    for "stack" - per OP terminology.

    The OP has coded pop() and push() methods, which are familiar/taught as
    'the' stack operations in every 'intro to algorithms' (or intermediate 'data-structures') class.

    Sadly, whereas there is list.pop(), the equivalent "push" method is
    append(). Thus, a (small) dissonance in one's mind.

    Writing a Stack class is unnecessary - at least given list, it is.
    However, having called the structure "stack" to be descriptive and
    helpful, it seems logical to continue by referring to the methods as
    "push" and "pop".
    (which is why many of us have such 'utilities'/snippets sitting-ready in
    a personal library)

    The OP has already coded such, so no cost to keep.


    (returning to the OP)
    Like @Gerard, am confused. There are two types: Pair and Empty.
    Thereafter the need is to keep them on a stack. The stack is a
    collection, an aggregate of 'whatever'. Why inherit from/to Stack?
    (is this a 'Lisp thing'?)

    --
    Regards,
    =dn

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Julieta Shem on Mon Oct 31 16:21:53 2022
    On Mon, 31 Oct 2022 at 14:38, Julieta Shem <jshem@yaxenu.org> wrote:

    Chris Angelico <rosuav@gmail.com> writes:

    The most straight-forward way to represent this concept in an object-oriented way is subclassing.

    class Stack:
    ... # put whatever code is common here

    class Empty(Stack):
    ... # put Empty-specific code here, possibly overriding Stack methods

    class Pair(Stack):
    ... # ditto, overriding or augmenting as needed

    This way, everything is an instance of Stack, but they are still
    distinct types for when you need to distinguish.

    Can you provide a small example? I can't see what you mean, but it
    seems interesting.

    Sure. The easiest way would be to take your existing Empty and Pair
    classes, have them subclass Stack, and don't bother putting any code
    at all into Stack. Then construct an Empty and a few Pairs, and what
    you'll see is that all of them are also instances of Stack.

    After that, it's really a question of what you expect to be able to do
    with either an Empty or a Pair. Anything that should be possible with
    both types (that is, anything that should be possible with either
    variant of Stack) should get moved into the Stack type, while anything
    that is specific to one or the other stays in its own class. So here's
    a very very simple example:

    class Stack:
    def prepend(self, other):
    return Pair(other, self)

    class Empty(Stack):
    def is_last(self):
    return True
    def get_current(self):
    raise ValueError("Stack empty")
    def get_next(self):
    raise ValueError("Stack empty")

    class Pair(Stack):
    def __init__(self, item1, item2):
    self.item1 = item1
    self.item2 = item2
    def get_current(self):
    return self.item1
    def get_next(self):
    return self.item2
    def is_last(self):
    return isinstance(self.item2, Empty)

    With this setup, you can build a stack by prepending items onto an
    Empty endpoint, and can iterate over it with the get_current and
    get_next methods. (Making this actually iterable, so that it works
    with a Python 'for' loop, would be a good exercise.)

    In this example, there isn't much code in the Stack class. But you
    could easily add more, and it would apply to both Empty and Pair.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Gauld@21:1/5 to Julieta Shem on Mon Oct 31 09:36:59 2022
    On 30/10/2022 14:01, Julieta Shem wrote:

    I wrote the classes

    class Empty:
    ...
    class Pair:
    ...

    (*) How to build a stack?

    These Lisp-like sequences are clearly a stack.

    That is a very important observation. A Pair IS-A Stack(sort of).
    If you had a stack you could create a Pair from it certainly.

    So far so good, but when it comes to building a better user interface
    for it I have no idea how to do it. I think one of the purposes of OOP
    is to organize code hierarchically so that we can reuse what we wrote.

    One of the purposes of classes certainly. I'm not so sure it's a purpose
    of OOP. They are not the same thing. A class is a programming construct
    OOP is a programming style. Classes facilitate OOP but can be used
    outside of OOP too.

    So I tried to make a Stack by inheriting Pair.

    But you said above that a Pair was a Stack. Inheritance implies an
    IS-A relationship, so Stack inheriting Pair would mean that a Stack
    was a Pair. That is not really true.

    A Stack could use a Pair (or many of them) but it is not a Pair.

    Trying to use inheritance inappropriately is one of the
    biggest (and commonest) mistakes in OOP. It invariably leads
    to complications. If in doubt use delegation instead.


    class Stack(Pair):
    pass

    Stack(1, Empty())
    Stack(1, Empty())

    Then I wrote pop and push.

    Stack(1, Empty()).pop()
    1

    Stack(1, Empty()).push(2)
    Stack(2, Stack(1, Empty()))

    So far so good. Now let me show you what I can't do.

    (*) The difficulty of encapsulating a union

    The Lisp-like sequences we're building here are union-like data
    structures. A /sequence/ is either Empty() or Pair(..., /sequence/). I
    have not found a way to represent this either-or datastructure with a
    class. For example, there is no way right now to build an empty Stack
    by invoking the Stack constructor.

    Stack()
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: Pair.__init__() missing 2 required positional arguments: 'first' and 'rest'


    The usual Python approach to such things is to put some default
    values(often None but could be an Empty instance in your case)
    in the init() method parameter list. Then test if the inputs
    are None and if so take the appropriate action.

    class Pair:
    def __init__(self, first=Empty(), rest=Empty()):

    Like this.

    if not isinstance(rest, Pair) and not isinstance(rest, Empty):
    raise ValueError("rest must be Empty or Pair")
    self.first = first
    self.rest = rest
    def fromIterable(it):
    if len(it) == 0:
    return Empty()
    else:
    return Pair(it[0], Pair.fromIterable(it[1:]))
    def __str__(self):
    return "{}({!r}, {})".format(self.__class__.__name__, self.first, str(self.rest))
    def __repr__(self):
    return str(self)
    def __len__(self):
    return 1 + self.rest.__len__()

    class Empty:
    def __len__(self):
    return 0
    def __str__(self):
    return "Empty()"
    def __repr__(self):
    return self.__str__()
    def __new__(clss):
    if not hasattr(clss, "saved"):
    clss.saved = super().__new__(clss)
    return clss.saved

    class Stack(Pair):
    def pop(self):
    return self.first
    def push(self, x):
    return Stack(x, self)

    --
    Alan G
    Author of the Learn to Program web site
    http://www.alan-g.me.uk/
    http://www.amazon.com/author/alan_gauld
    Follow my photo-blog on Flickr at:
    http://www.flickr.com/photos/alangauldphotos

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Gerard" on Mon Oct 31 12:40:36 2022
    "Weatherby,Gerard" <gweatherby@uchc.edu> writes:
    To answer your question generally, Union’s are not OO.

    What's more non-OO to me here is immutability.

    The functions/methods of the OP do not actually
    change a stack but they return a new stack. This
    style is being used in purely functional programming.

    Object-oriented programming, however, uses objects
    that have a state which may change in time. The whole
    definition of a stack talks about how it is being
    /modified/ by push and pop operations. So, I think that
    object-oriented programming embraces change. At least
    one can say that OOP by itself does not encourage
    immutability even though sometimes one wants to have
    immutability for some other reasons (for example,
    immutability may help in parallel programming).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Gerard" on Mon Oct 31 14:14:57 2022
    "Weatherby,Gerard" <gweatherby@uchc.edu> writes:
    I don’t understand your implementation enough to comment specifically. Wh= >at’s the definition of Pair? (i.e. what methods and public attributes doe= >s it have?) (I manage to escape Lisp as an undergrad)

    When I learned LISP, it was still spelled "LISP". Below,
    I am using the traditional LISP terms as I learned them,
    while modern dialects may use different terms and names.

    The peculiar usage of spaces is my own style and not
    necessarily used by other LISP programmers.

    A pair in LISP is the same as a pair in math or in English!

    The low-level basic kind of a pair in LISP is called a
    "dotted pair", because it is written as "( A . B )"
    (to tell it from the list "( A B )", which is written
    without a dot and is not the same as the dotted pair
    "( A . B )").

    A dotted pair is created with CONS, and one could take as
    a definition the interrelation between CONS, CAD, and CDR,
    which is:

    ( CAR( CONS A B )) is x, where x is the result of the evaluation of A
    ( CDR( CONS A B )) is y, where y is the result of the evaluation of B

    . LISP uses a modified Cambridge notation by which the
    application of the function f to the arguments x, y, z is
    written as "( f x y z )" and not as "f( x, y, z )".

    The evaluation of "( CONS A B )" yields the dotted pair
    ( x . y ), where
    x is the result of the evaluation of A and
    y is the the result of the evaluation of B.

    Dotted pairs are used to build lists. For example, the
    list (B A) is represented by

    ( B . ( A . NULL ))

    where NULL is a special value used to mark the end of
    a list. NULL can also be written as "()".

    |
    V
    .---.---. .---.---.
    | B | o----->| A | o---->|
    '---'---' '---'---'

    So,

    ( C . ( B A ))

    is the list

    ( C B A )

    |
    V
    .---.---. .---.---. .---.---.
    | C | o----->| B | o----->| A | o---->|
    '---'---' '---'---' '---'---'

    . The terminating NULL is not shown in the list notation,
    just as the terminating \0 is not shown for a C string.
    But in dotted pair notation the last list is

    ( C .( B .( A . NULL )))

    .

    Application programmers in LISP usually would not think much
    about the "lower tier" of dotted pairs that is used to implement
    lists. So a pair of values in LISP usually would be implemented
    by an application programmer as the list "( A B )", which internally
    would be stored by the nesting of dotted pairs which is

    "( A .( B . NULL ))"

    |
    V
    .---.---. .---.---.
    | B | o----->| A | o---->|
    '---'---' '---'---'

    .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Alan Gauld on Tue Nov 1 14:58:54 2022
    Alan Gauld <learn2program@gmail.com> writes:

    On 30/10/2022 14:01, Julieta Shem wrote:

    I wrote the classes

    class Empty:
    ...
    class Pair:
    ...

    (*) How to build a stack?

    These Lisp-like sequences are clearly a stack.

    That is a very important observation. A Pair IS-A Stack(sort of).
    If you had a stack you could create a Pair from it certainly.

    Yes, they are the same thing --- in my mind ---, except possibly for the
    user interface, names and stuff. I do prefer to make the user think he
    has something else simply because it has a different name. It is not
    the user's business to know what things are on the inside.

    So far so good, but when it comes to building a better user interface
    for it I have no idea how to do it. I think one of the purposes of OOP
    is to organize code hierarchically so that we can reuse what we wrote.

    One of the purposes of classes certainly. I'm not so sure it's a purpose
    of OOP. They are not the same thing. A class is a programming construct
    OOP is a programming style. Classes facilitate OOP but can be used
    outside of OOP too.

    That's an interesting observation. I like that. Classes are one thing
    and OOP is another. Of course, at the extreme I think I will get
    nowhere in trying to detect in high-precision what is OOP and what is
    not. The same for classes. I always liked to think of C structures as
    some class. Instead of saying ``new Whatever()'', I'd say
    ``malloc(sizeof ...)''. I'd then have many procedures whose first
    argument were a pointer to Whatever. They're the methods. So the
    structure Whatever si the class itself. Is this use of C outside of
    OOP? I say it is not because my notion of OOP is that --- a way to make objects and have methods operate on them, changing them or not.

    Should I allow the non-change of objects to be OOP as well? I think so.
    To me what distinguishes functional from imperative is, for example,
    imperative has side-effects. We can take an imperative set of tools and
    do not produce any side-effects (locally speaking, of course) and that
    would be indisguishable from functional programming. We can say that imperative languages are more general than functional ones. (That's my non-educated guess for the moment anyhow.)

    So I tried to make a Stack by inheriting Pair.

    But you said above that a Pair was a Stack. Inheritance implies an
    IS-A relationship, so Stack inheriting Pair would mean that a Stack
    was a Pair. That is not really true.

    That's another interesting observation. I do not have much
    understanding of how to really think of these things and that's most
    likely why I never tried to understand OOP or imperative programming.
    It seems very difficult.

    What I meant is that a Pair was a Stack and a Stack was a Pair. The
    reason I was creating a Stack at all was just to make the user think
    it's something different --- and with more obvious names for the case
    under analysis, which was the use of a stack-like data structure.

    A Stack could use a Pair (or many of them) but it is not a Pair.

    Is this how I should design it? I tried that, actually. It seemed to complicate my life because I had to provide to my Stack class various
    other methods my Pair class already had.

    Trying to use inheritance inappropriately is one of the
    biggest (and commonest) mistakes in OOP. It invariably leads
    to complications. If in doubt use delegation instead.

    What is delegation?

    I think that's why I never went into OOP and imperative languages. It
    just seems bloody difficult to get anything right. Call me stupid or
    what you will, but it seems very difficult.

    Any book recomendations on getting this thing mathematics-clear?

    Thank you!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Tue Nov 1 18:11:25 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    clarify. If I wish for an empty stack, I wish I could just say
    Stack()
    Stack()
    and if I wish for a nonempty stack, I'd write
    Stack(1, Stack(2, Stack(3, Stack())))
    Stack(1, Stack(2, Stack(3, Stack())))

    If this is all,

    main.py

    class Stack:
    def __init__( self, *args ):
    self.data = [ args[ 0 ], args[ 1 ]]if len( args ) else []
    def __str__( self ):
    if len( self.data ):
    return f"Stack({self.data[0]}, {self.data[1]})"
    else:
    return f"Stack()"

    print( Stack() )

    print( Stack(1, Stack(2, Stack(3, Stack()))) )

    output

    Stack()
    Stack(1, Stack(2, Stack(3, Stack())))

    .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Chris Angelico on Tue Nov 1 14:46:48 2022
    Chris Angelico <rosuav@gmail.com> writes:

    On Mon, 31 Oct 2022 at 14:38, Julieta Shem <jshem@yaxenu.org> wrote:

    Chris Angelico <rosuav@gmail.com> writes:

    The most straight-forward way to represent this concept in an
    object-oriented way is subclassing.

    class Stack:
    ... # put whatever code is common here

    class Empty(Stack):
    ... # put Empty-specific code here, possibly overriding Stack methods >> >
    class Pair(Stack):
    ... # ditto, overriding or augmenting as needed

    This way, everything is an instance of Stack, but they are still
    distinct types for when you need to distinguish.

    Can you provide a small example? I can't see what you mean, but it
    seems interesting.

    Sure. The easiest way would be to take your existing Empty and Pair
    classes, have them subclass Stack, and don't bother putting any code
    at all into Stack. Then construct an Empty and a few Pairs, and what
    you'll see is that all of them are also instances of Stack.

    After that, it's really a question of what you expect to be able to do
    with either an Empty or a Pair. Anything that should be possible with
    both types (that is, anything that should be possible with either
    variant of Stack) should get moved into the Stack type, while anything
    that is specific to one or the other stays in its own class. So here's
    a very very simple example:

    class Stack:
    def prepend(self, other):
    return Pair(other, self)

    class Empty(Stack):
    def is_last(self):
    return True
    def get_current(self):
    raise ValueError("Stack empty")
    def get_next(self):
    raise ValueError("Stack empty")

    class Pair(Stack):
    def __init__(self, item1, item2):
    self.item1 = item1
    self.item2 = item2
    def get_current(self):
    return self.item1
    def get_next(self):
    return self.item2
    def is_last(self):
    return isinstance(self.item2, Empty)

    With this setup, you can build a stack by prepending items onto an
    Empty endpoint, and can iterate over it with the get_current and
    get_next methods. (Making this actually iterable, so that it works
    with a Python 'for' loop, would be a good exercise.)

    In this example, there isn't much code in the Stack class. But you
    could easily add more, and it would apply to both Empty and Pair.

    Thank you so much for the example. It's very interesting as it sort of reverses my intuition. It seems non-obvious. I had in mind to build a
    Stack out of Empty and Pair, but you seem to have done it the other way
    around.

    But I still don't see a way of using your classes up there in which I
    have a single name to build empty and non-empty stacks. Let me
    clarify. If I wish for an empty stack, I wish I could just say

    Stack()
    Stack()

    and if I wish for a nonempty stack, I'd write

    Stack(1, Stack(2, Stack(3, Stack())))
    Stack(1, Stack(2, Stack(3, Stack())))

    With your classes, I think I must write

    Empty() # That's an empty stack
    ...

    Pair(1, Pair(2, Empty())) # That's a non-empty stack
    ...

    In other words, I need to memorize two different names for using stacks.
    I'm trying to have a single name for both parts of the union (that is my
    design of a stack).

    Thanks so much!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Tue Nov 1 18:28:14 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    Any book recomendations on getting this thing mathematics-clear?

    OOP cannot be mathematics-clear because it is an /attempt/ to
    abstract from several different programming languages.

    When you ask Alan Kay (and I did!), the man who coined the term
    "object-oriented", he will tell you (in my words):
    "Object-oriented programming is possible in Smalltalk only.".

    So, to get OOP crystal clear from the one who coined the
    term, go ahead an learn Smalltalk!

    But while Kay coined the term, many ideas of OOP are based
    on Simula. So, you might think about learning Simula to get
    the Dahl-flavor of OOP.

    This is comp.lang.python. Here we do Python programming.

    10 or 20 years ago there were still famous people, such as
    Robert C. Martin, hanging around in "comp.object", and I would
    have told you to go there, but today that newsgroup is deserted.

    Here's an excerpt from an older post by me, written in May this year:

    In 2003, I became aware of the fact that Alan Kay, the man
    who coined the term "object-oriented programming" in 1967
    (or in the temporal proximity of this year), never has given
    a definition for it. I asked him via e-mail, and he kindly
    responded. In that e-mail he also wrote something to the effect
    that for him only Smalltalk allows object-oriented programming.
    (On another occasion, he also said, "I invented the term Object-
    Oriented and I can tell you I did not have C++ in mind.".) So,
    I think that this point of view by Alan Kay is similar to what
    Liam wrote about Smalltalk!

    So, what did Alan Kay write to me in 2003? Here's the crucial excerpt:

    |OOP to me means only messaging, local retention and protection and
    |hiding of state-process, and extreme late-binding of all things. It
    |can be done in Smalltalk and in LISP. There are possibly other
    |systems in which this is possible, but I'm not aware of them.
    Alan Kay, 2003

    . I should add that the deepest insight I gained into what is the
    actual point of OOP (as I wrote in my previous post in this thread)
    I got from the writings of Robert C. Martin who clarified that
    OOP makes it easy to add new types but hard to add new operations,
    while procedural programming makes it easy to add new operations,
    but hard to add new types.

    |Procedural code (code using data structures) makes it easy to
    |add new functions without changing the existing data
    |structures. OO code, on the other hand, makes it easy to add
    |new classes without changing existing functions.
    Robert Cecil Martin

    |Procedural code makes it hard to add new data structures
    |because all the functions must change. OO code makes it hard
    |to add new functions because all the classes must change.
    Robert Cecil Martin

    When one first reads this, it might not be obvious why this
    is so spot on, but one can find this quotation in the book
    "Clean Code" by Robert C. Martin and read more explanations
    about it there.

    Objects with data abstraction can already be found in CLU by
    Barbara Liskov. But this is not yet OOP. The crucial feature
    OOP adds to this is polymorphism ("late binding").

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Stefan Ram on Tue Nov 1 19:34:50 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    The crucial feature OOP adds to this is polymorphism ("late binding").

    If polymorphism is so crucial, the idea of subclasses
    has something to it! So here's another way to code your
    requirements, using subclasses:

    main.py

    class Stack:
    def __new__( self, *args ):
    return nonempty_stack( *args )if len( args ) else empty_stack()
    def __str__( self ):
    return str( self.data )

    class nonempty_stack( Stack ):
    def __new__( self, *args ):
    return object().__new__( self )
    def __init__( self, *args ):
    self.data =[ args[ 0 ], args[ 1 ]]
    def __str__( self ):
    return f"Stack({self.data[0]}, {self.data[1]})"

    class empty_stack( Stack ):
    def __new__( self, *args ):
    return object().__new__( self )
    def __init__( self, *args ):
    self.data = []
    def __str__( self ):
    return f"Stack()"

    print( Stack() )
    print( Stack(1, Stack(2, Stack(3, Stack()))) )

    print()
    print( type( Stack() ))
    print( type( Stack(1, Stack(2, Stack(3, Stack()))) ))

    output

    Stack()
    Stack(1, Stack(2, Stack(3, Stack())))

    <class '__main__.empty_stack'>
    <class '__main__.nonempty_stack'>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Tue Nov 1 17:54:37 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:
    clarify. If I wish for an empty stack, I wish I could just say
    Stack()
    Stack()
    and if I wish for a nonempty stack, I'd write
    Stack(1, Stack(2, Stack(3, Stack())))
    Stack(1, Stack(2, Stack(3, Stack())))

    If this is all,

    main.py

    class Stack:
    def __init__( self, *args ):
    self.data = [ args[ 0 ], args[ 1 ]]if len( args ) else []
    def __str__( self ):
    if len( self.data ):
    return f"Stack({self.data[0]}, {self.data[1]})"
    else:
    return f"Stack()"

    print( Stack() )

    print( Stack(1, Stack(2, Stack(3, Stack()))) )

    output

    Stack()
    Stack(1, Stack(2, Stack(3, Stack())))

    Thanks! But we've left behind a more basic requirement --- the Stack
    class wishes for all the methods already written in some class called
    Pair, which has a certain connection with a class Empty. This
    requirement has been implicit in the first message in this thread,
    though. I didn't make it explicit. (In other words, this part of the
    thread only makes sense by taking into account all the previous messages
    up in the thread.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Weatherby,Gerard@21:1/5 to Julieta Shem on Tue Nov 1 20:32:33 2022
    I think:

    class Stack:
    def __init__( self, *args ):
    self.data = args

    def __str__( self ):
    return f"Stack({','.join(str(x) for x in self.data)})"

    gives equivalent output for the if len(args) is 0 or 2, if it’s okay for self.data to be a tuple.

    class Stack:
    def __init__( self, *args ):
    self.data = list(args)

    def __str__( self ):
    return f"Stack({','.join(str(x) for x in self.data)})"

    If it’s desired self.data be a list.


    From: Python-list <python-list-bounces+gweatherby=uchc.edu@python.org> on behalf of Stefan Ram <ram@zedat.fu-berlin.de>
    Date: Tuesday, November 1, 2022 at 3:43 PM
    To: python-list@python.org <python-list@python.org>
    Subject: Re: an oop question
    *** Attention: This is an external email. Use caution responding, opening attachments or clicking on links. ***

    Julieta Shem <jshem@yaxenu.org> writes:
    clarify. If I wish for an empty stack, I wish I could just say
    Stack()
    Stack()
    and if I wish for a nonempty stack, I'd write
    Stack(1, Stack(2, Stack(3, Stack())))
    Stack(1, Stack(2, Stack(3, Stack())))

    If this is all,

    main.py

    class Stack:
    def __init__( self, *args ):
    self.data = [ args[ 0 ], args[ 1 ]]if len( args ) else []
    def __str__( self ):
    if len( self.data ):
    return f"Stack({self.data[0]}, {self.data[1]})"
    else:
    return f"Stack()"

    print( Stack() )

    print( Stack(1, Stack(2, Stack(3, Stack()))) )

    output

    Stack()
    Stack(1, Stack(2, Stack(3, Stack())))

    .


    -- https://urldefense.com/v3/__https://mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!itEYnwU5jJ0z8_rkW_q_ogw3ZJUNdHdMNkMLpSAqBdozBNrr7NqPs_gNsbx8W9uXRLZpG38C9an17Yx2zUf-mSA$<https://urldefense.com/v3/__https:/mail.python.org/mailman/listinfo/
    python-list__;!!Cn_UX_p3!itEYnwU5jJ0z8_rkW_q_ogw3ZJUNdHdMNkMLpSAqBdozBNrr7NqPs_gNsbx8W9uXRLZpG38C9an17Yx2zUf-mSA$>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Tue Nov 1 17:56:41 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    The crucial feature OOP adds to this is polymorphism ("late binding").

    If polymorphism is so crucial, the idea of subclasses
    has something to it! [...]

    I wonder what Rich Hickey would say here. Didn't he design Clojure with ``multimethods'' so that we can get some kind of polymorphism without subclassing?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Ewing@21:1/5 to Julieta Shem on Wed Nov 2 19:10:05 2022
    On 2/11/22 9:54 am, Julieta Shem wrote:
    But we've left behind a more basic requirement --- the Stack
    class wishes for all the methods already written in some class called
    Pair,

    Is that *really* what you want, though?

    To my way of thinking, a Stack and a Pair are quite different
    data structures, each having their own characteristic operations.

    Things I want to be able to do with a Stack:
    - Create an empty stack
    - Push an item onto the top of the stack
    - Pop an item off the top of the stack

    Things I want to be able to do with a Pair:
    - Create a pair containing two given objects
    - Get the first item
    - Get the second item

    I don't see any overlap between these at the conceptual level.
    Possibly I might want to use Pairs as part of the *implementation*
    of a stack, but that doesn't mean that any Pair methods should
    appear as Stack methods.

    Here's how I might do this in a functional style using Python:

    class Pair:
    def __init__(self, first, second):
    self._first = first
    self._second = second
    def first(self):
    return self._first
    def second(self):
    return self._second

    class Stack:
    def __init__(self):
    self._data = None
    def push(self, item):
    result = Stack()
    result._data = Pair(item, self._data)
    return result
    def pop(self):
    rest = Stack()
    rest._data = self._data.second()
    return self._data.first(), rest

    Note that neither Stack nor Pair inherits from the other.

    --
    Greg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Wed Nov 2 09:07:28 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    The crucial feature OOP adds to this is polymorphism ("late binding").
    If polymorphism is so crucial, the idea of subclasses
    has something to it! [...]
    I wonder what Rich Hickey would say here. Didn't he design Clojure with >``multimethods'' so that we can get some kind of polymorphism without >subclassing?

    Yes. In general, one can have polymorphism without subclassing, but
    in class-based OOP, such as in Python, inheritance is the standard
    way to enable polymorphism. In other languages, such as Clojure, Self,
    or the original JavaScript, one might use other means than inheritance.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Wed Nov 2 09:02:02 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    Thanks! But we've left behind a more basic requirement --- the Stack
    class wishes for all the methods already written in some class called
    Pair, which has a certain connection with a class Empty. This
    requirement has been implicit in the first message in this thread,
    though. I didn't make it explicit. (In other words, this part of the
    thread only makes sense by taking into account all the previous messages
    up in the thread.)

    Sorry! So, I combine the "Stack" class from my other post with
    your two classes "Pair" and "Empty":

    main.py

    class Stack:
    def __new__( self, *args ):
    return Pair( args[ 0 ], args[ 1 ])if args else Empty()

    class Pair:
    def __init__(self, first, rest):
    if not isinstance(rest, Pair) and not isinstance(rest, Empty):
    raise ValueError("rest must be Empty or Pair")
    self.first = first
    self.rest = rest
    def fromIterable(it):
    if len(it) == 0:
    return Empty()
    else:
    return Pair(it[0], Pair.fromIterable(it[1:]))
    def __str__(self):
    return "{}({!r}, {})".format(self.__class__.__name__, self.first, str(self.rest))
    def __repr__(self):
    return str(self)
    def __len__(self):
    return 1 + self.rest.__len__()

    class Empty:
    def __len__(self):
    return 0
    def __str__(self):
    return "Empty()"
    def __repr__(self):
    return self.__str__()
    def __new__(clss):
    if not hasattr(clss, "saved"):
    clss.saved = super().__new__(clss)
    return clss.saved

    print( Stack() )
    print( Stack(1, Stack(2, Stack(3, Stack()))) )

    print()
    print( type( Stack() ))
    print( type( Stack(1, Stack(2, Stack(3, Stack()))) ))

    output:

    Empty()
    Pair(1, Pair(2, Pair(3, Empty())))

    <class '__main__.Empty'>
    <class '__main__.Pair'>

    You also could have inherit them from "Stack",
    but then they would need a custom __new__ method
    with "return object().__new__( self )".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Greg Ewing on Wed Nov 2 09:19:30 2022
    Greg Ewing <greg.ewing@canterbury.ac.nz> writes:
    I don't see any overlap between these at the conceptual level.

    It might come from early LISP dialects. In early LISPs, the
    only basic means to combine data into a larger assembly of
    data was the dotted pair and NULL (as an end marker). So,
    whenever you wanted to create a data structure, you would
    wonder how to build it from dotted pairs and NULLs.

    You also had lists, of course, but these were themselves build
    from dotted pairs and NULLs, as I explained in a recent post.

    (I wrote "early LISP", because I don't know much about modern
    "Lisp" dialects.)

    And when people learn a new (programming) language, until they
    become more confident in it, they start to emulate their
    previous language(s) in it. Writing FORTRAN in Pascal, and
    so on. So when you know Clojure and then come to Python, you
    might wonder how to do Clojure things in Python. (Clojure
    is a kind of Lisp.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Stefan Ram on Wed Nov 2 09:56:28 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    Yes. In general, one can have polymorphism without subclassing, but
    in class-based OOP, such as in Python, inheritance is the standard
    way to enable polymorphism.

    I was wrong!

    In Python, we *don't* need inheritance/subclassing for polymorphism!

    By "polymorphism", I mean that I can send the same message to
    objects of different types, as in:

    def f( language ):
    print( language.greeting() )

    . Above, the message "greeting()" is being send to the
    object "language". The type of "language" is not fixed
    by the source code, and this is polymorphism.

    Now, I can write a full program with two subclasses of
    "Language":

    main.py

    class Language:
    def greeting():
    pass

    class English( Language ):
    def greeting( self ):
    return "Good morning!"

    class French( Language ):
    def greeting( self ):
    return "Bonjour !"

    def f( language ):
    print( language.greeting() )

    f( English() )
    f( French() )

    output

    Good morning!
    Bonjour !

    . The reader first should try to understand how the output
    comes about.

    Now, in the next program, I have removed the subclassings,
    there is no inheritance from the base class "Language"
    anymore. Yet the polymorphism in "f" still works. And this
    shows that in Python we do *not* need subclassing/inheritance
    for polymorphism!

    main.py

    class Language:
    def greeting():
    pass

    class English:
    def greeting( self ):
    return "Good morning!"

    class French:
    def greeting( self ):
    return "Bonjour !"

    def f( language ):
    print( language.greeting() )

    f( English() )
    f( French() )

    output

    Good morning!
    Bonjour !

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Gauld@21:1/5 to Julieta Shem on Wed Nov 2 17:52:11 2022
    On 01/11/2022 17:58, Julieta Shem wrote:

    nowhere in trying to detect in high-precision what is OOP and what is
    not.

    Stefan has given a good answer but essentially OOP is a program
    that is structured around objects communicating by sending
    messages to one another.

    Objects are, in most (but not all) languages implemented using classes.
    A class is just a record or structure that can contain data and
    functions. In most (but not all) implementations of OOP messages
    are implemented as calls to the functions within an objects class.


    The same for classes. I always liked to think of C structures as
    some class.

    Provided that we allow pointers to functions within the struct then yes.
    Indeed the difference between structs and classes in C++ is very thin.

    And with the recent introduction of data classes in Python the
    boundaries are blurred here too. In pure OOP a class with only
    data would be pointless(since the data should ideally be hidden
    or "private"!)

    structure Whatever si the class itself. Is this use of C outside of
    OOP? I say it is not because my notion of OOP is that --- a way to make objects and have methods operate on them, changing them or not.

    It's how many early attempts at OOP in C worked, including C++.
    Others, such as Objective C and cFlavours took a different approach.

    But conceptually methods do not "operate on objects"
    methods are how objects respond to messages. Eah object has its own
    method of handling a particular message. (This is polymorphism)
    In most (but not all) practical implementations methods are
    usually implemented as functions and so it seems like a message
    is just a synonym for acalling a method, but is more than that, it
    implies some kind of dynamic lookup. (for example when the method is implemented in a paremt class rather than the directly referenced one.

    But the functions that represent methods do indeed operate on their
    own object - ie self.

    To me what distinguishes functional from imperative is,

    Not that imperative programming is not the same as OOP. Or at
    least it encompasses much more than OOP. Most OOP programs are
    written in imperative languages but it is possible to have
    functional OOP programs too. (If we consider the state data
    to be a single compound value that can only be changed by
    the object internally, or a new object with different
    state values returned.)

    IS-A relationship, so Stack inheriting Pair would mean that a Stack
    was a Pair. That is not really true.

    That's another interesting observation. I do not have much
    understanding of how to really think of these things

    Read up on the Liskoff substitution principle as one approach to
    determining what an IS-A relationship means. Its not the only
    approach but it is less vague than some others!

    to complications. If in doubt use delegation instead.

    What is delegation?

    A fancy OOP term to mean containment.
    Specifically a method delegates the work top some internal
    object. eg. You can build a stack by containg a list within
    it. The stack delegates much of the work to the list object.
    In fact, in Python a stack can be a very thin wrapper over
    a list!

    Any book recomendations on getting this thing mathematics-clear?

    The best book is IMHO Bertrand Meyer's book "Object Oriented
    Software Construction". It uses his own language,. Eiffel, but
    gives an excellent description of applied OOP and all of its
    features (Eiffel is perhaps the most feature complete OOPL of
    all - but, being proprietary (and somewhat buggy!) it has
    never quite caught on)

    But for a mathematical basis you need to look outside programming
    at systems engineering and the work done on cellular automata.
    In a pure OOP program each object should act as an independant
    cell communicating by asynchronous messages. Exactly as in
    cellular automata theory. ..

    Unfortunately, there is a huge gap between pure OOP theory and
    practical OOP languages! And just as big a gap between OOP language
    potential and real-world OOP usage. Very few purely OOP programs
    exist (maybe excepting in Smalltalk - see Stefan's posts again). :-(

    --
    Alan G
    Author of the Learn to Program web site
    http://www.alan-g.me.uk/
    http://www.amazon.com/author/alan_gauld
    Follow my photo-blog on Flickr at:
    http://www.flickr.com/photos/alangauldphotos

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dennis Lee Bieber@21:1/5 to All on Wed Nov 2 16:21:25 2022
    On 2 Nov 2022 09:56:28 GMT, ram@zedat.fu-berlin.de (Stefan Ram) declaimed
    the following:


    Now, in the next program, I have removed the subclassings,
    there is no inheritance from the base class "Language"
    anymore. Yet the polymorphism in "f" still works. And this
    shows that in Python we do *not* need subclassing/inheritance
    for polymorphism!

    To me, that is not really an example of polymorphism, but more an example of Python's "duck typing".

    I'd implement the example hierarchy as

    class Language:
    ... def f(self):
    ... print(self.greeting)
    ...
    class English(Language):
    ... def __init__(self):
    ... self.greeting = "Good Morning"
    ...
    class French(Language):
    ... def __init__(self):
    ... self.greeting = "Bonjour"
    ...
    English().f()
    Good Morning
    French().f()
    Bonjour

    ... with no explicit /function/ for greeting -- it's just an attribute set in each subtype, inheriting the "f" function for printing.


    --
    Wulfraed Dennis Lee Bieber AF6VN
    wlfraed@ix.netcom.com http://wlfraed.microdiversity.freeddns.org/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Gauld@21:1/5 to Dennis Lee Bieber on Thu Nov 3 00:15:05 2022
    On 02/11/2022 20:21, Dennis Lee Bieber wrote:

    shows that in Python we do *not* need subclassing/inheritance
    for polymorphism!

    To me, that is not really an example of polymorphism, but more an example of Python's "duck typing".

    But duck typing is a perfectly good implementation of polymorphism.
    The term just means that different objects respond to the same
    message in different ways. Nothing more, nothing less. Inheritance
    just happens to be the most common way of building that, especially
    in statically typed languages.


    I'd implement the example hierarchy as

    class Language:
    ... def f(self):
    ... print(self.greeting)

    And that would be perfectly valid too.

    --
    Alan G
    Author of the Learn to Program web site
    http://www.alan-g.me.uk/
    http://www.amazon.com/author/alan_gauld
    Follow my photo-blog on Flickr at:
    http://www.flickr.com/photos/alangauldphotos

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Wed Nov 2 21:45:46 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Greg Ewing <greg.ewing@canterbury.ac.nz> writes:
    I don't see any overlap between these at the conceptual level.

    It might come from early LISP dialects. In early LISPs, the
    only basic means to combine data into a larger assembly of
    data was the dotted pair and NULL (as an end marker). So,
    whenever you wanted to create a data structure, you would
    wonder how to build it from dotted pairs and NULLs.

    You also had lists, of course, but these were themselves build
    from dotted pairs and NULLs, as I explained in a recent post.

    (I wrote "early LISP", because I don't know much about modern
    "Lisp" dialects.)

    And when people learn a new (programming) language, until they
    become more confident in it, they start to emulate their
    previous language(s) in it. Writing FORTRAN in Pascal, and
    so on. So when you know Clojure and then come to Python, you
    might wonder how to do Clojure things in Python. (Clojure
    is a kind of Lisp.)

    And that's very interesting. It's a nice evidence of that conjecture
    that the languages we use affect the way we see things. Noam Chomsky
    has been saying that the primary function of language is to be a tool of thought rather than communication; communication is definitely important
    and real, but not the primary reason why language was developed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Dennis Lee Bieber on Wed Nov 2 21:35:01 2022
    Dennis Lee Bieber <wlfraed@ix.netcom.com> writes:

    On 2 Nov 2022 09:56:28 GMT, ram@zedat.fu-berlin.de (Stefan Ram) declaimed
    the following:


    Now, in the next program, I have removed the subclassings,
    there is no inheritance from the base class "Language"
    anymore. Yet the polymorphism in "f" still works. And this
    shows that in Python we do *not* need subclassing/inheritance
    for polymorphism!

    To me, that is not really an example of polymorphism, but more an example of Python's "duck typing".

    I'd implement the example hierarchy as

    class Language:
    ... def f(self):
    ... print(self.greeting)
    ...
    class English(Language):
    ... def __init__(self):
    ... self.greeting = "Good Morning"
    ...
    class French(Language):
    ... def __init__(self):
    ... self.greeting = "Bonjour"
    ...
    English().f()
    Good Morning
    French().f()
    Bonjour

    ... with no explicit /function/ for greeting -- it's just an attribute set in each subtype, inheriting the "f" function for printing.

    A popular encyclopedia would enumerate various specifications of the
    word polymorphism. Ad hoc polymorphism, parametric polymorphim, subtype polymorphim et cetera.

    ``One of the most difficult matters in all controversy is to
    distinguish disputes about words from disputes about facts: it ought
    not to be difficult, but in practice it is.''
    -- ABC of Relativity, Bertrand Russell, chapter 12, 1925.

    ``What's in a name?''
    -- Romeo and Juliet, Shakespeare, 1597.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Greg Ewing on Wed Nov 2 21:37:51 2022
    Greg Ewing <greg.ewing@canterbury.ac.nz> writes:

    On 2/11/22 9:54 am, Julieta Shem wrote:
    But we've left behind a more basic requirement --- the Stack
    class wishes for all the methods already written in some class called
    Pair,

    Is that *really* what you want, though?

    To my way of thinking, a Stack and a Pair are quite different
    data structures, each having their own characteristic operations.

    Things I want to be able to do with a Stack:
    - Create an empty stack
    - Push an item onto the top of the stack
    - Pop an item off the top of the stack

    Things I want to be able to do with a Pair:
    - Create a pair containing two given objects
    - Get the first item
    - Get the second item

    I don't see any overlap between these at the conceptual level.
    Possibly I might want to use Pairs as part of the *implementation*
    of a stack, but that doesn't mean that any Pair methods should
    appear as Stack methods.

    The code for computing the length of a Pair (which is really a linked
    list) happens to be the same for computing the length of a Stack. My
    class Pair has methods map, filter, reduce, ... Stacks could use them
    too.

    Here's how I might do this in a functional style using Python:

    class Pair:
    def __init__(self, first, second):
    self._first = first
    self._second = second
    def first(self):
    return self._first
    def second(self):
    return self._second

    class Stack:
    def __init__(self):
    self._data = None
    def push(self, item):
    result = Stack()
    result._data = Pair(item, self._data)
    return result
    def pop(self):
    rest = Stack()
    rest._data = self._data.second()
    return self._data.first(), rest

    Note that neither Stack nor Pair inherits from the other.

    That works too.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Alan Gauld on Wed Nov 2 21:53:33 2022
    Alan Gauld <learn2program@gmail.com> writes:

    On 01/11/2022 17:58, Julieta Shem wrote:

    [...]

    IS-A relationship, so Stack inheriting Pair would mean that a Stack
    was a Pair. That is not really true.

    That's another interesting observation. I do not have much
    understanding of how to really think of these things

    Read up on the Liskoff substitution principle as one approach to
    determining what an IS-A relationship means. Its not the only
    approach but it is less vague than some others!

    Thanks so much for the references. I'll definitely look up the Liskov substitution principle and try to understand it. More on this soon.
    Thanks also for the Meyer's reference too, the explanation of
    ``delegation'' and all your comments. I appreciate it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Wed Nov 2 21:25:30 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:
    Any book recomendations on getting this thing mathematics-clear?

    OOP cannot be mathematics-clear because it is an /attempt/ to
    abstract from several different programming languages.

    When you ask Alan Kay (and I did!), the man who coined the term
    "object-oriented", he will tell you (in my words):
    "Object-oriented programming is possible in Smalltalk only.".

    That's very interesting. Would you share the complete thread of e-mail?
    I would love to read it word for word.

    So, to get OOP crystal clear from the one who coined the
    term, go ahead an learn Smalltalk!

    After your message, I looked up

    THE EARLY HISTORY OF SMALLTALK
    https://dl.acm.org/doi/pdf/10.1145/234286.1057828

    and gave it a read. Very nice read.

    But while Kay coined the term, many ideas of OOP are based
    on Simula. So, you might think about learning Simula to get
    the Dahl-flavor of OOP.

    This is comp.lang.python. Here we do Python programming.

    The impression I got from Smalltalk in the paper above was that Python
    is so much based on it.

    10 or 20 years ago there were still famous people, such as
    Robert C. Martin, hanging around in "comp.object", and I would
    have told you to go there, but today that newsgroup is deserted.

    So sad.

    Here's an excerpt from an older post by me, written in May this year:

    In 2003, I became aware of the fact that Alan Kay, the man
    who coined the term "object-oriented programming" in 1967
    (or in the temporal proximity of this year), never has given
    a definition for it. I asked him via e-mail, and he kindly
    responded. In that e-mail he also wrote something to the effect
    that for him only Smalltalk allows object-oriented programming.
    (On another occasion, he also said, "I invented the term Object-
    Oriented and I can tell you I did not have C++ in mind.".) So,
    I think that this point of view by Alan Kay is similar to what
    Liam wrote about Smalltalk!

    So, what did Alan Kay write to me in 2003? Here's the crucial excerpt:

    |OOP to me means only messaging, local retention and protection and
    |hiding of state-process, and extreme late-binding of all things. It
    |can be done in Smalltalk and in LISP. There are possibly other
    |systems in which this is possible, but I'm not aware of them.
    Alan Kay, 2003

    I'm wondering how Python fails to satisfy his definition.

    . I should add that the deepest insight I gained into what is the
    actual point of OOP (as I wrote in my previous post in this thread)
    I got from the writings of Robert C. Martin who clarified that
    OOP makes it easy to add new types but hard to add new operations,
    while procedural programming makes it easy to add new operations,
    but hard to add new types.

    |Procedural code (code using data structures) makes it easy to
    |add new functions without changing the existing data
    |structures. OO code, on the other hand, makes it easy to add
    |new classes without changing existing functions.
    Robert Cecil Martin

    |Procedural code makes it hard to add new data structures
    |because all the functions must change. OO code makes it hard
    |to add new functions because all the classes must change.
    Robert Cecil Martin

    When one first reads this, it might not be obvious why this
    is so spot on, but one can find this quotation in the book
    "Clean Code" by Robert C. Martin and read more explanations
    about it there.

    Thank you for the reference. His comments make sense to me, although
    it's not crystal-clear. I'll eventually read his book.

    Objects with data abstraction can already be found in CLU by
    Barbara Liskov. But this is not yet OOP. The crucial feature
    OOP adds to this is polymorphism ("late binding").

    That's helpful. I saw Alan Kay talking about late binding in the paper
    I mentioned above. Thank you so much.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Ewing@21:1/5 to Julieta Shem on Thu Nov 3 14:47:31 2022
    On 3/11/22 1:37 pm, Julieta Shem wrote:
    The code for computing the length of a Pair (which is really a linked
    list) happens to be the same for computing the length of a Stack.

    I would question whether that should be a method of Pair at all,
    since it's not the length of the pair itself, but the length of
    a structure built out of pairs.

    But in any case, the way to do this in a conceptually clean way
    is for the length method of Stack to call whatever it is that
    computes the length of a linked list of Pairs. This is what
    the term "delegation" refers to.

    --
    Greg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Alan Gauld on Thu Nov 3 09:20:46 2022
    Alan Gauld <learn2program@gmail.com> writes:
    On 02/11/2022 20:21, Dennis Lee Bieber wrote:
    shows that in Python we do *not* need subclassing/inheritance
    for polymorphism!
    To me, that is not really an example of polymorphism, but more an
    example of Python's "duck typing".
    But duck typing is a perfectly good implementation of polymorphism.

    This is a good insight; I'll try to remember this!

    I have my own wording, by which I say that in the case of
    polymorphism, messages actually are send to /expressions/
    (and only later to objects).

    One could /start/ writing a program with:

    def f( language ):
    print( language.greeting() )

    . No object "language" already exists, but the code already
    contains the instruction ".greeting()" to send a message.
    The receiver is written before it: "language". There's no
    object yet. "language" is an expression.

    Then, classes "English" and "French" could be defined later,
    and "f" then could be called using "f( English() )" and
    "f( French() )". So upon execution of this, an object would
    be behind the expression at run-time as required for execution.

    But at write-time (when the programmer writes the code of "f"),
    there do not have to be objects, but expressions.

    "Polymorphism" then means that an expression, such as "language",
    at run-time can evaluate to objects of types (μοÏϕή) which may
    differ (πολυ-) on different evaluations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Thu Nov 3 09:37:08 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    That's very interesting. Would you share the complete thread of e-mail?
    I would love to read it word for word.

    Yes, with pleasure! A quotation from my corresponding web page:

    (For technical reasons, the web page today gives
    "https://www.purl.org/stefan_ram/pub/doc_kay_oop_en" as the
    "canonical URI", but both should work. I prefer the old one
    with "http:", but am afraid that one day "http" might stop
    to work.)

    Dr. ALAN KAY on the Meaning of “Object-Oriented Programmingâ€

    (To link to this page, please use the canonical URI "http://purl.net/stefan_ram/pub/doc_kay_oop_en" only,
    because any other URI is valid only temporarily.)

    E-Mail of 2003-07-23

    Dr. ALAN KAY was so kind as to answer my questions about the
    term “object-oriented programmingâ€.

    Clarification of "object-oriented" [E-Mail]

    Date: Wed, 23 Jul 2003 09:33:31 -0800
    To: Stefan Ram [removed for privacy]
    From: Alan Kay [removed for privacy]
    Subject: Re: Clarification of "object-oriented"
    [some header lines removed for privacy]
    Content-Type: text/plain; charset="us-ascii" ; format="flowed"
    Content-Length: 4965
    Lines: 117

    Hi Stefan --

    Sorry for the delay but I was on vacation.

    At 6:27 PM +0200 7/17/03, Stefan Ram wrote:
    Dear Dr. Kay,

    I would like to have some authoritative word on the term
    "object-oriented programming" for my tutorial page on the
    subject. The only two sources I consider to be "authoritative"
    are the International Standards Organization, which defines
    "object-oriented" in "ISO/IEC 2382-15", and you, because,
    as they say, you have coined that term.

    I'm pretty sure I did.


    Unfortunately, it is difficult to find a web page or source
    with your definition or description of that term. There are
    several reports about what you might have said in this regard
    (like "inheritance, polymorphism and encapsulation"), but
    these are not first-hand sources. I am also aware that later
    you put more emphasis on "messaging" - but I still would like
    to know about "object oriented".

    For the records, my tutorial page, and further distribution
    and publication could you please explain:

    When and where was the term "object-oriented" used first?

    At Utah sometime after Nov 66 when, influenced by Sketchpad, Simula,
    the design for the ARPAnet, the Burroughs B5000, and my background in
    Biology and Mathematics, I thought of an architecture for
    programming. It was probably in 1967 when someone asked me what I was
    doing, and I said: "It's object-oriented programming".

    The original conception of it had the following parts.

    - I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so
    messaging came at the very beginning -- it took a while to see how to
    do messaging in a programming language efficiently enough to be
    useful).

    - I wanted to get rid of data. The B5000 almost did this via its
    almost unbelievable HW architecture. I realized that the
    cell/whole-computer metaphor would get rid of data, and that "<-"
    would be just another message token (it took me quite a while to
    think this out because I really thought of all these symbols as names
    for functions and procedures.

    - My math background made me realize that each object could have
    several algebras associated with it, and there could be families of
    these, and that these would be very very useful. The term
    "polymorphism" was imposed much later (I think by Peter Wegner) and
    it isn't quite valid, since it really comes from the nomenclature of
    functions, and I wanted quite a bit more than functions. I made up a
    term "genericity" for dealing with generic behaviors in a
    quasi-algebraic form.

    - I didn't like the way Simula I or Simula 67 did inheritance
    (though I thought Nygaard and Dahl were just tremendous thinkers and designers). So I decided to leave out inheritance as a built-in
    feature until I understood it better.

    My original experiments with this architecture were done using a
    model I adapted from van Wijngaarten's and Wirth's "Generalization of
    Algol" and Wirth's Euler. Both of these were rather LISP-like but
    with a more conventional readable syntax. I didn't understand the
    monster LISP idea of tangible metalanguage then, but got kind of
    close with ideas about extensible languages draw from various
    sources, including Irons' IMP.

    The second phase of this was to finally understand LISP and then
    using this understanding to make much nicer and smaller and more
    powerful and more late bound understructures. Dave Fisher's thesis
    was done in "McCarthy" style and his ideas about extensible control
    structures were very helpful. Another big influence at this time was
    Carl Hewitt's PLANNER (which has never gotten the recognition it
    deserves, given how well and how earlier it was able to anticipate
    Prolog).

    The original Smalltalk at Xerox PARC came out of the above. The
    subsequent Smalltalk's are complained about in the end of the History
    chapter: they backslid towards Simula and did not replace the
    extension mechanisms with safer ones that were anywhere near as
    useful.


    What does "object-oriented [programming]" mean to you?
    (No tutorial-like introduction is needed, just a short
    explanation [like "programming with inheritance,
    polymorphism and encapsulation"] in terms of other concepts
    for a reader familiar with them, if possible. Also, it is
    not neccessary to explain "object", because I already have
    sources with your explanation of "object" from
    "Early History of Smalltalk".)

    (I'm not against types, but I don't know of any type systems that
    aren't a complete pain, so I still like dynamic typing.)

    OOP to me means only messaging, local retention and protection and
    hiding of state-process, and extreme late-binding of all things. It
    can be done in Smalltalk and in LISP. There are possibly other
    systems in which this is possible, but I'm not aware of them.

    Cheers,

    Alan


    Thank you,

    Stefan Ram


    --
    E-Mail of 2003-07-26
    Clarification of "object-oriented", 1 [E-Mail]

    Date: Sat, 26 Jul 2003 13:47:59 -0800
    To: Stefan Ram [removed for privacy]
    From: Alan Kay [removed for privacy]
    Subject: Re: Clarification of "object-oriented"
    [some header lines removed for privacy]
    Content-Type: text/plain; charset="us-ascii" ; format="flowed"
    Content-Length: 3145
    Lines: 68

    One of the things I should have mentioned is that there were two main
    paths that were catalysed by Simula. The early one (just by accident)
    was the bio/net non-data-procedure route that I took. The other one,
    which came a little later as an object of study was abstract data
    types, and this got much more play.

    If we look at the whole history, we see that the proto-OOP stuff
    started with ADT, had a little fork towards what I called "objects"
    -- that led to Smalltalk, etc.,-- but after the little fork, the CS establishment pretty much did ADT and wanted to stick with the
    data-procedure paradigm. Historically, it's worth looking at the USAF
    Burroughs 220 file system (that I described in the Smalltalk
    history), the early work of Doug Ross at MIT (AED and earlier) in
    which he advocated embedding procedure pointers in data structures,
    Sketchpad (which had full polymorphism -- where e.g. the same offset
    in its data structure meant "display" and there would be a pointer to
    the appropriate routine for the type of object that structure
    represented, etc., and the Burroughs B5000, whose program reference
    tables were true "big objects" and contained pointers to both "data"
    and "procedures" but could often do the right thing if it was trying
    to go after data and found a procedure pointer. And the very first
    problems I solved with my early Utah stuff was the "disappearing of
    data" using only methods and objects. At the end of the 60s (I think)
    Bob Balzer wrote a pretty nifty paper called "Dataless Programming",
    and shortly thereafter John Reynolds wrote an equally nifty paper
    "Gedanken" (in 1970 I think) in which he showed that using the lamda expressions the right way would allow data to be abstracted by
    procedures.

    The people who liked objects as non-data were smaller in number, and
    included myself, Carl Hewitt, Dave Reed and a few others -- pretty
    much all of this group were from the ARPA community and were involved
    in one way or another with the design of ARPAnet->Internet in which
    the basic unit of computation was a whole computer. But just to show
    how stubbornly an idea can hang on, all through the seventies and
    eighties, there were many people who tried to get by with "Remote
    Procedure Call" instead of thinking about objects and messages. Sic
    transit gloria mundi.

    Cheers,

    Alan

    At 10:05 PM +0200 7/26/03, Stefan Ram wrote:
    On Wed, Jul 23, 2003 at 09:33:31AM -0800, Alan Kay wrote:
    OOP to me means only messaging, local retention and protection and
    hiding of state-process, and extreme late-binding of all things.

    Hi Alan,

    I just want to say "thank you" for your explanations
    (including the parts not quoted above)!

    "local retention" is a new notion to me in the context
    of OOP, and I assume it refers to state-process and
    means that an object is in possession of its state-process,
    so that the state of an object is kept locally with the
    object and not elsewhere.

    I have published your reply on the web, but have removed
    the E-Mail addresses and similar header lines for privacy.

    Thanks again,

    Stefan


    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Thu Nov 3 10:33:45 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    ...
    |Procedural code (code using data structures) makes it easy to
    |add new functions without changing the existing data
    |structures. OO code, on the other hand, makes it easy to add
    |new classes without changing existing functions.
    Robert Cecil Martin
    ...
    Thank you for the reference. His comments make sense to me, although
    it's not crystal-clear. I'll eventually read his book.

    You're welcome!

    Let me try to clarify this with an example in Python.

    1. Procedural-Style "emit" Procedure:

    class Number:
    def __init__( self, value ):
    self.value = value

    class String:
    def __init__( self, value ):
    self.text = value

    def emit( object ):
    if type( object )== Number:
    print( object.value )
    elif type( object )== String:
    print( object.text )

    emit( Number( 123 ))
    emit( String( "abc" ))

    1. a. Adding a New Type in Procedural Programming

    In procedural programming, as above, adding a new type,
    such as "class Widget", is hard because the procedure "emit"
    might be a standard procedure contained in some standard
    library, and we would have to add a new "elif type" branch
    to that procedure to be able to also emit widgets. This
    violates the Open-Closed-Principle: "Objects or entities
    should be open for extension but closed for modification.".
    We would need to /modify/ "emit" which should be closed
    for modification!

    1. b. Adding a New Procedure in Procedural Programming

    Adding a new procedure in procedural programming is easy,
    I can just write it and start to use it:

    def length( object ):
    if type( object )== Number:
    return len( str( object.value ))
    elif type( object )== String:
    return len( object.text )

    2. Object-oriented Style "emit" Verb:

    (By "verb", I mean the selector of a message,
    i.e., the name of a method, such as "emit" below.)

    class Number:
    def __init__( self, value ):
    self.value = value
    def emit( self ):
    print( self.value )

    class String:
    def __init__( self, value ):
    self.text = value
    def emit( self ):
    print( self.text )

    Number( 123 ).emit()
    String( "abc" ).emit()

    2. a. Adding a New Type in Object-Oriented Programming

    In object-oriented programming, adding a new type is easy,
    one just writes it and may then starts to use it. One does
    not need to modify anything else, so one complies with
    the Open-Close-Principle:

    class Number:
    def __init__( self, value ):
    self.value = value
    def emit( self ):
    print( self.value )

    class String:
    def __init__( self, value ):
    self.text = value
    def emit( self ):
    print( self.text )

    class Widget:
    def __init__( self, value ):
    self.data = value
    def emit( self ):
    print( "Widget( " + self.data + " )" )

    Number( 123 ).emit()
    String( "abc" ).emit()
    Widget( "example" ).emit()

    2. a. 1. Remark

    One can observe this ease especially when one defines a new
    class with a standard verb and then standard procedures
    "magically" use this new method, as in:

    class MyNewClass:
    def __str__( self ):
    return "Howdy!"

    print( MyNewClass() )

    How can "print" possibly know about the method "__str__"
    I just defined if "print" was written long before I defined
    my class? <-- A beginner could ask this in bewilderment!

    2. b. Adding a New Verb (Procedure) in Object-Oriented Programming

    In object-oriented programming adding a new verb (a new
    "procedure") is hard. Assume that now we would like to add
    another verb such as "emit", say "length". All classes would
    have to be changed and a new method definition for "length"
    would have to be added to them! Some classes might even be
    standard classes from libraries we can't easily change.
    So this clearly violates the Open-Closed-Principle!

    3. Comments

    So, this would suggest to use procedural programming when
    one foresees the need to add more object-specific procedures
    later and object-oriented programming when one foresees the
    need to add more types later.

    The problems with OOP which make adding new verbs violate
    the open-closed principle possibly would not occur in
    a language where one could add new methods to a library
    class in a user program.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Gauld@21:1/5 to Julieta Shem on Thu Nov 3 10:43:28 2022
    On 03/11/2022 00:25, Julieta Shem wrote:

    |OOP to me means only messaging, local retention and protection and
    |hiding of state-process, and extreme late-binding of all things.

    I'm wondering how Python fails to satisfy his definition.

    Python doesn't do any form of data protection/hiding. All
    attributes are public by default.

    In Smalltalk all attributes are private, with no way to
    make them public... Actually in C++/Java terms I believe
    they are "protected" because subclasses can access
    them(I think?).

    Also Python is not a purely OOP language, in that you can write
    functional and procedural code in Python if you wish. In
    Smalltalk thats notionally impossible because everything
    is an object. And all programming statements are messages
    to objects.

    Even an if/else test is a message to the boolean object:

    3) ifTrue: <block of code>
    ifFalse: <block of code>

    ifTrue is a message to the boolean result of the expression
    which has a parameter of a block of code. It executes the
    block if the boolean is true.
    ifFalse is likewise a message to the same boolean object
    but only executes its block if the boolean is false.

    (Apologies if the syntax is out, its been a while since I
    wrote any Smalltalk!)

    Similarly loops are implemented as messages:

    <boolean> whileTrue: <block>
    <Number> to: <Number> do: <block>

    So in Smalltalk absolutely everything is a message to an object.

    Python by comparison is much more traditional in form.

    It is possible to write procedural, non OOP code in
    Smalltalk but you really have to fight the system to
    do so.

    --
    Alan G
    Author of the Learn to Program web site
    http://www.alan-g.me.uk/
    http://www.amazon.com/author/alan_gauld
    Follow my photo-blog on Flickr at:
    http://www.flickr.com/photos/alangauldphotos

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Weatherby,Gerard@21:1/5 to Julieta Shem on Thu Nov 3 11:07:19 2022
    C++/Java class variables can be public, protected (accessible to class and subclasses) or private (accessible only to class). Of course the language protections can be hacked around.

    Python does conceptual private variables by using the single underscore: object._myvar is considered private

    From: Python-list <python-list-bounces+gweatherby=uchc.edu@python.org> on behalf of Alan Gauld <learn2program@gmail.com>
    Date: Thursday, November 3, 2022 at 6:45 AM
    To: Julieta Shem <jshem@yaxenu.org>, python-list@python.org <python-list@python.org>
    Subject: Re: an oop question
    *** Attention: This is an external email. Use caution responding, opening attachments or clicking on links. ***

    On 03/11/2022 00:25, Julieta Shem wrote:

    |OOP to me means only messaging, local retention and protection and
    |hiding of state-process, and extreme late-binding of all things.

    I'm wondering how Python fails to satisfy his definition.

    Python doesn't do any form of data protection/hiding. All
    attributes are public by default.

    In Smalltalk all attributes are private, with no way to
    make them public... Actually in C++/Java terms I believe
    they are "protected" because subclasses can access
    them(I think?).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Thu Nov 3 11:12:22 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    I'll definitely look up the Liskov
    substitution principle and try to understand it.

    I found the LSP to be very confusing:

    First, it's was hard for me to actually get a clear source
    (citation) for it. What exactly are the words of Barbara
    Liskov that are called "LSP"?

    Then, I found some words that that might possibly be the LSP
    in the words of Barbara Liskov:

    |If for each object o1 of type S there is an object o2 of
    |type T such that for all programs P defined in terms of T,
    |the behavior of P is unchanged when o1 is substituted for o2
    |then S is a subtype of T.

    This uses nested quantifiers, somewhat like

    ( ∀(o1∈S) ∃(o2∈T) ∀(P∈P(T)) B(P(o2))=(P(o1)) )==> S < T

    . And such a proposition is hard for me to understand!

    Later, I looked at a book in a bookstore; it was a book
    about programming by Barbara Liskov that came out after the
    LSP was already mentioned often, and as far as I could see,
    that book did not mention the LSP at all, although it
    actually had a chapter about subtypes!

    So, here's my personal principle, that I use instead of the LSP:

    An object-oriented program is not complete without proper
    documentation, i.e., contracts. The documentation of a class
    must be true for all objects of this class and for all objects
    of all direct and indirect subclasses.

    . If this was too long, one could abbreviate this to just:

    Class contracts must hold for subclasses.

    . I think the original LSP unfortunately tried to avoid
    references to contracts and just talks about code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Alan Gauld on Thu Nov 3 22:50:23 2022
    On Thu, 3 Nov 2022 at 21:44, Alan Gauld <learn2program@gmail.com> wrote:
    Also Python is not a purely OOP language, in that you can write
    functional and procedural code in Python if you wish. In
    Smalltalk thats notionally impossible because everything
    is an object. And all programming statements are messages
    to objects.

    In Python, everything is an object. Doesn't that equally mean that
    Python is purely OOP?

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Greg Ewing on Thu Nov 3 09:29:20 2022
    Greg Ewing <greg.ewing@canterbury.ac.nz> writes:

    On 3/11/22 1:37 pm, Julieta Shem wrote:
    The code for computing the length of a Pair (which is really a linked
    list) happens to be the same for computing the length of a Stack.

    I would question whether that should be a method of Pair at all,
    since it's not the length of the pair itself, but the length of
    a structure built out of pairs.

    You make a lot of sense.

    But in any case, the way to do this in a conceptually clean way
    is for the length method of Stack to call whatever it is that
    computes the length of a linked list of Pairs. This is what
    the term "delegation" refers to.

    Thank you for the example. I agree with you. Perhaps I can reduce the
    class Pair to just being a pair as we know it, made of two things, then
    we create a class called Sequence, which is really a composition of
    Pairs, comes with a length method and we derive Stack from it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Alan Gauld on Thu Nov 3 12:27:44 2022
    Alan Gauld <learn2program@gmail.com> writes:
    Even an if/else test is a message to the boolean object:
    3) ifTrue: <block of code>
    ifFalse: <block of code>

    Yes, and this means that one can define new control
    structures using OOP in Smalltalk.

    To define "ifTrue", one would define a class "Boolean"
    with two subclasses "True" and "False". In "True":

    ifTrue: aBlock
    ^aBlock value

    , in "False" the same selector "ifTrue" is defined as:

    ifTrue: aBlock
    ^nil

    (polymorphism!).

    . Now, one can write:

    a < 0 ifTrue: [a := 0]

    and, if "a < 0" is true, then the definition of the
    class "True" will be called for "ifTrue", otherwise
    the definition from the class "False".

    An attempt to emulate this in Python:

    main.py

    class True_:
    def if_true( self, block ):
    exec( block )

    class False_:
    def if_true( self, block ):
    pass

    class Boolean_:
    def __new__( self, value ):
    return True_() if value else False_()

    a = -2; Boolean_( a < 0 ).if_true( "print( 'It is true!' )" )
    a = +2; Boolean_( a < 0 ).if_true( "print( 'It is true!' )" )

    output

    It is true!

    .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Chris Angelico on Thu Nov 3 09:57:57 2022
    Chris Angelico <rosuav@gmail.com> writes:

    On Thu, 3 Nov 2022 at 21:44, Alan Gauld <learn2program@gmail.com> wrote:
    Also Python is not a purely OOP language, in that you can write
    functional and procedural code in Python if you wish. In
    Smalltalk thats notionally impossible because everything
    is an object. And all programming statements are messages
    to objects.

    In Python, everything is an object. Doesn't that equally mean that
    Python is purely OOP?

    I think Alan Gauld pointed out that even syntax is an object in
    Smalltalk --- or was. An if-statement in Python is not an object.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Thu Nov 3 09:41:54 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:
    That's very interesting. Would you share the complete thread of e-mail?
    I would love to read it word for word.

    Yes, with pleasure! A quotation from my corresponding web page:

    (For technical reasons, the web page today gives
    "https://www.purl.org/stefan_ram/pub/doc_kay_oop_en" as the
    "canonical URI", but both should work. I prefer the old one
    with "http:", but am afraid that one day "http" might stop
    to work.)

    [...]

    Nice! Thank you so much!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Julieta Shem on Fri Nov 4 05:29:25 2022
    On Fri, 4 Nov 2022 at 05:21, Julieta Shem <jshem@yaxenu.org> wrote:

    Chris Angelico <rosuav@gmail.com> writes:

    On Thu, 3 Nov 2022 at 21:44, Alan Gauld <learn2program@gmail.com> wrote:
    Also Python is not a purely OOP language, in that you can write
    functional and procedural code in Python if you wish. In
    Smalltalk thats notionally impossible because everything
    is an object. And all programming statements are messages
    to objects.

    In Python, everything is an object. Doesn't that equally mean that
    Python is purely OOP?

    I think Alan Gauld pointed out that even syntax is an object in
    Smalltalk --- or was. An if-statement in Python is not an object.

    Okay, fair; although I would be highly surprised if syntax is actually
    an object ("flow control is an object", I would believe, though). Is
    the concept "pass this message to this object" an object? Is the
    message itself an object? Is it objects all the way down?

    At some point, any language with objects in it is "object oriented" to
    some extent, and after that, it's all a spectrum. Some people claim
    that Java is "more object-oriented" than Python because all Java code
    has to be in a class, and others counteract by saying that Python is
    "more object-oriented" because every value in Python is a subclass of
    object and has a type which is a subclass of type. I'm sure that
    someone somewhere has claimed that Python "isn't object-oriented" on
    the basis that len(x) is the WRONG way to put it, and it should be
    x.length() instead.

    On second thoughts, it's not a single spectrum, it's a
    multidimensional thing that's so tangled up that it guarantees that
    people can happily debate for years to come.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Thu Nov 3 15:48:41 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:

    [...]

    2. a. 1. Remark

    One can observe this ease especially when one defines a new
    class with a standard verb and then standard procedures
    "magically" use this new method, as in:

    class MyNewClass:
    def __str__( self ):
    return "Howdy!"

    print( MyNewClass() )

    How can "print" possibly know about the method "__str__"
    I just defined if "print" was written long before I defined
    my class? <-- A beginner could ask this in bewilderment!

    That's a good question.

    2. b. Adding a New Verb (Procedure) in Object-Oriented Programming

    In object-oriented programming adding a new verb (a new
    "procedure") is hard. Assume that now we would like to add
    another verb such as "emit", say "length". All classes would
    have to be changed and a new method definition for "length"
    would have to be added to them! Some classes might even be
    standard classes from libraries we can't easily change.
    So this clearly violates the Open-Closed-Principle!

    3. Comments

    So, this would suggest to use procedural programming when
    one foresees the need to add more object-specific procedures
    later and object-oriented programming when one foresees the
    need to add more types later.

    The problems with OOP which make adding new verbs violate
    the open-closed principle possibly would not occur in
    a language where one could add new methods to a library
    class in a user program.

    Thank you! That did clarify everything for me! (I'll find a way to
    read Martin's book!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Thu Nov 3 15:51:12 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:
    I'll definitely look up the Liskov >>substitution principle and try to understand it.

    I found the LSP to be very confusing:

    First, it's was hard for me to actually get a clear source
    (citation) for it. What exactly are the words of Barbara
    Liskov that are called "LSP"?

    Then, I found some words that that might possibly be the LSP
    in the words of Barbara Liskov:

    |If for each object o1 of type S there is an object o2 of
    |type T such that for all programs P defined in terms of T,
    |the behavior of P is unchanged when o1 is substituted for o2
    |then S is a subtype of T.

    This uses nested quantifiers, somewhat like

    ( ∀(o1∈S) ∃(o2∈T) ∀(P∈P(T)) B(P(o2))=(P(o1)) )==> S < T

    . And such a proposition is hard for me to understand!

    For me too.

    Later, I looked at a book in a bookstore; it was a book
    about programming by Barbara Liskov that came out after the
    LSP was already mentioned often, and as far as I could see,
    that book did not mention the LSP at all, although it
    actually had a chapter about subtypes!

    Do you remember the book's title?

    So, here's my personal principle, that I use instead of the LSP:

    An object-oriented program is not complete without proper
    documentation, i.e., contracts. The documentation of a class
    must be true for all objects of this class and for all objects
    of all direct and indirect subclasses.

    That's much easier to ``parse'' indeed. Thanks for the contribution. (<joke>The empty documentation seems to satisfy the principle.</joke>)

    . If this was too long, one could abbreviate this to just:

    Class contracts must hold for subclasses.

    . I think the original LSP unfortunately tried to avoid
    references to contracts and just talks about code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alan Gauld@21:1/5 to Chris Angelico on Fri Nov 4 00:09:59 2022
    On 03/11/2022 18:29, Chris Angelico wrote:
    On Fri, 4 Nov 2022 at 05:21, Julieta Shem <jshem@yaxenu.org> wrote:

    Chris Angelico <rosuav@gmail.com> writes:

    On Thu, 3 Nov 2022 at 21:44, Alan Gauld <learn2program@gmail.com> wrote: >>>> Also Python is not a purely OOP language, in that you can write
    functional and procedural code in Python if you wish. In
    Smalltalk thats notionally impossible because everything
    is an object. And all programming statements are messages
    to objects.

    In Python, everything is an object. Doesn't that equally mean that
    Python is purely OOP?

    I think Alan Gauld pointed out that even syntax is an object in
    Smalltalk --- or was. An if-statement in Python is not an object.

    Okay, fair; although I would be highly surprised if syntax is actually
    an object

    The syntax isn't, it is a specification, but the AST certainly
    is and you can instantiate it and examine it from code.

    But the context here was Why *Alan Kay* doesn't include Python
    with Smalltalk as being OOP. There are plenty others who would
    call it so.

    But as for everything being an object, that's true but it doesn't
    alter the fact that the default mode of python programming is not
    to structure the code as a set of communicating objects (even if
    at some level everything is an object) but as a set of
    hierarchical functions.

    And fundamentally that's what Kay means by OOP. The program (not
    the language!) is built around objects. One of the greatest
    barriers to the adoption of OOP is the confusion between OOP
    and OOPL. And sadly the majority of attention has been on OOPL
    rather than OOP...

    the concept "pass this message to this object" an object? Is the
    message itself an object? Is it objects all the way down?

    At some point you hit C/Assembler. But it is astonishing just
    how far down the objects go in Smalltalk.

    But the control flow etc are fully OOP as per my last message.
    It if quite possible to subclass Boolean and override the
    whileTrue method to do something completely different to
    the normal while loop behaviour. Is it wise? No. But it
    is definitely possible!

    At some point, any language with objects in it is "object oriented" to
    some extent, and after that, it's all a spectrum.

    And this is exactly the problem. In the 80s we had a fairly clean
    concensus of what OOP meant and several different language approaches
    to that. But when C++ became popular(the defacto standard) peple started focussing on the language features and totally missed that Object
    Oriented Programming means writing programs that consist of objects communicating by messages. The language features(even classes) are
    merely implementation details. (I suspect this was partly driven
    by the fact that many university lecturers at the time didn't
    really grok OOP and teaching language features was much easier
    than teaching a new way of thinking about problems!)

    This was compounded when someone (Booch maybe?) came up with a set
    of criteria to determine whether a language was a "true OOPL" or
    merely "Object Based" (Ada and VB they're looking at you!) But
    frankly that was an irrelevance to OOP. You can do OOP (and I
    have!) in assembler and in COBOL - you just need to follow
    some agreed ground rules. It's a lot easier with an OOPL but
    it's not required.

    multidimensional thing that's so tangled up that it guarantees that
    people can happily debate for years to come.
    Exactly so. During the 90's there came to be at least 3 different
    camps of OOP evangelists and the OOP world slowly coalesced on
    a kind of hybrid amalgam with multiple names for the same feature
    and disagrements about which features are required or not to
    really be OOP. And as a result the whole concept of OOP has
    been muddied to the point that almost nobody does it anymore
    apart from (possibly) the Smalltalk crowd who have always
    smugly sat above the general throng content in the knowledge
    that theirs is the one true OOP! :-)

    Which brings us back to Alan Kay...


    --
    Alan G
    Author of the Learn to Program web site
    http://www.alan-g.me.uk/
    http://www.amazon.com/author/alan_gauld
    Follow my photo-blog on Flickr at:
    http://www.flickr.com/photos/alangauldphotos

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Weatherby,Gerard@21:1/5 to All on Fri Nov 4 01:19:37 2022
  • From Greg Ewing@21:1/5 to All on Fri Nov 4 20:06:37 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes [that Barbara Liskov said]:

    |If for each object o1 of type S there is an object o2 of
    |type T such that for all programs P defined in terms of T,
    |the behavior of P is unchanged when o1 is substituted for o2
    |then S is a subtype of T.

    That seems overly restrictive, because it wouldn't allow S to
    override a method of T and make it do something different --
    which we do all the time in practice.

    Class contracts must hold for subclasses.

    That sounds like a much better way of saying it!

    --
    Greg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Ewing@21:1/5 to Chris Angelico on Fri Nov 4 20:01:56 2022
    On 4/11/22 12:50 am, Chris Angelico wrote:
    In Python, everything is an object. Doesn't that equally mean that
    Python is purely OOP?

    Depends on what you mean by "purely oop". To me it suggests a
    language in which dynamically-dispatched methods are the only
    form of code. Python is not one of those, because it has
    stand-alone functions.

    I'm not sure I know of *any* language that is purely oop in
    that sense. Smalltalk probably comes the closest, but then its
    code blocks are essentially lexically-scoped anonymous functions.
    You *could* write Smalltalk code in a procedural style by
    assigning a bunch of code blocks to names and calling them like
    functions. Not that there would be any reason to do that other
    than as a party trick.

    Java looks like it's fairly purely OO at first glance, but it
    has static methods, which are really stand-alone functions by
    another name. Also it has some primitive types such as ints
    and floats that don't behave in an OO way at all.

    --
    Greg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Ewing@21:1/5 to Julieta Shem on Fri Nov 4 20:09:03 2022
    On 4/11/22 7:51 am, Julieta Shem wrote:

    (<joke>The empty documentation seems to satisfy the principle.</joke>)

    All the more reason to document your classes!

    More seriously, there's always at least a (possibly fuzzily) implied
    contract, because of the names you choose for things if nothing else.

    --
    Greg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Ewing@21:1/5 to Julieta Shem on Fri Nov 4 20:11:50 2022
    On 4/11/22 1:29 am, Julieta Shem wrote:
    Perhaps I can reduce the
    class Pair to just being a pair as we know it, made of two things, then
    we create a class called Sequence, which is really a composition of
    Pairs, comes with a length method and we derive Stack from it.

    That sounds better. But be careful -- a Sequence class would
    probably be expected to have methods for e.g. inserting and
    removing things in the middle, which you might not want to
    allow for a Stack.

    --
    Greg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Julieta Shem on Fri Nov 4 08:04:01 2022
    Julieta Shem <jshem@yaxenu.org> writes:
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    Later, I looked at a book in a bookstore; it was a book
    about programming by Barbara Liskov that came out after the
    LSP was already mentioned often, and as far as I could see,
    that book did not mention the LSP at all, although it
    actually had a chapter about subtypes!
    Do you remember the book's title?

    Unfortunately, no. I might have seen it between 1995 and 2010,
    so it could have been

    Program Development in Java:
    Abstraction, Specification, and Object-Oriented Design
    by John Guttag and Barbara Liskov,
    June 2000

    . It has this predecessor:

    Abstraction and Specification in Program Development
    by Barbara Liskov and John Guttag,
    March 1986

    . But I also found this paper, which I recommend to look at
    in this regard:

    A Behavioral Notion of Subtyping,
    Barbara Liskov and Jeannette M. Wing,
    1994

    . It contains the quite readable

    |Subtype Requirement: Let φ(z) 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 also can be found on the Wikipedia page about the
    "Liskov substitution principle". The Wikipedia page has
    chosen one quotation as /the/ LSP while it is not so clear
    that this specific wording was what people actually referred
    to 20 years ago when they spoke of "the LSP". A page from
    "c2.com" (the first Wiki, the Wikipedia of its times)
    in 2003 instead gave the complicated wording I quoted before
    as "the LSP":

    |Liskov Substitution Principle
    |Barbara Liskov first wrote the LSP as follows in 1988: What
    |is wanted here is something like the following substitution
    |property: If for each object o1 of type S there is an object
    |o2 of type T [...]
    |Barbara Liskov, Data Abstraction and Hierarchy, SIGPLAN
    |Notices, 23,5 (May, 1988).
    from "c2.com" in 2003, page "Liskov Substitution Principle"

    So, Wikipedia gives you one wording, c2.com a different
    wording, which means that it is not clear what the actual
    wording of "the LSP" is.

    From the Wikipedia page:

    |While widely used, the characterization of behavioral
    |subtyping as the ability to substitute subtype objects for
    |supertype objects has been said to be flawed[citation
    |needed]. It makes no mention of specifications, so it
    |invites an incorrect reading where the implementation of
    |the supertype is compared to the implementation of the
    |subtype.
    Wikipedia page "Liskov substitution principle"

    |In an interview in 2016, Liskov herself explains that what
    |she presented in her keynote address was an "informal rule",
    |that Jeannette Wing later proposed that they "try to figure
    |out precisely what this means", which led to their joint
    |publication[1] on behavioral subtyping, and indeed that
    |"technically, it's called behavioral subtyping".[3] During
    |the interview, she does not use substitution terminology to
    |discuss the concepts.
    Wikipedia page "Liskov substitution principle"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Greg Ewing on Sat Nov 5 02:25:00 2022
    On Sat, 5 Nov 2022 at 02:18, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:

    On 4/11/22 12:50 am, Chris Angelico wrote:
    In Python, everything is an object. Doesn't that equally mean that
    Python is purely OOP?

    Depends on what you mean by "purely oop". To me it suggests a
    language in which dynamically-dispatched methods are the only
    form of code. Python is not one of those, because it has
    stand-alone functions.


    Define "stand-alone". In Java, all code has to be associated with a
    class file, but they can be static methods. A Java-like language in
    which classes are themselves first-class objects (and thus static
    methods are instance methods on the class) would, in a sense, have
    nothing but dynamically-dispatched methods as the only form of code.
    It wouldn't be materially different from regular Java though (at
    least, not for the sake of this purity; having first-class classes
    would probably have other benefits).

    Pike code, like Java code, is always associated with an object or a
    program (Pike's name for a class). However, built-in functions
    (implemented in C) can stand entirely alone. Would Pike become "purely
    OOP" if all standalone builtins were deemed to be methods of some
    global object? It wouldn't materially change anything.

    Maybe it's one of those terms that is useless for actual coding
    (because practicality beats purity), but good for discussions?

    Good for arguments, at least.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Greg Ewing on Sat Nov 5 02:26:53 2022
    On Sat, 5 Nov 2022 at 02:21, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:

    ram@zedat.fu-berlin.de (Stefan Ram) writes [that Barbara Liskov said]:

    |If for each object o1 of type S there is an object o2 of
    |type T such that for all programs P defined in terms of T,
    |the behavior of P is unchanged when o1 is substituted for o2
    |then S is a subtype of T.

    That seems overly restrictive, because it wouldn't allow S to
    override a method of T and make it do something different --
    which we do all the time in practice.

    Class contracts must hold for subclasses.

    That sounds like a much better way of saying it!


    Yeah, I would agree with that latter definition. The trouble is that
    few programs - and fewer programmers - really define which parts are
    contracts, so it's much easier to say "a subclass behaves just like
    the superclass(es) do(es)" - but it would be more accurate to qualify
    that with "in the ways that the superclass(es) define as standard
    behaviour".

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Greg Ewing on Fri Nov 4 21:58:19 2022
    Greg Ewing <greg.ewing@canterbury.ac.nz> writes:

    On 4/11/22 1:29 am, Julieta Shem wrote:
    Perhaps I can reduce the
    class Pair to just being a pair as we know it, made of two things, then
    we create a class called Sequence, which is really a composition of
    Pairs, comes with a length method and we derive Stack from it.

    That sounds better. But be careful -- a Sequence class would
    probably be expected to have methods for e.g. inserting and
    removing things in the middle, which you might not want to
    allow for a Stack.

    I guess we could override those and raise NotImplementedError?

    I don't actually know how to do that. Say I have those two classes
    Empty and Pair. Here's a way to design Seq:

    class Seq:
    class SeqEmpty(Empty):
    pass
    class SeqPair(Pair):
    def insert(self):
    return ...
    def remove(self):
    return ...
    def __new__(clss, *args):
    if len(args) == 0:
    return Seq.SeqEmpty()
    else:
    return Seq.SeqPair(*args)

    How do I override insert in Stack?

    class Stack(Seq):
    def insert(self):
    raise NotImplementedError

    That doesn't work because when I create an object of type Stack, it is
    actually an object of type Seq.SeqEmpty or of type Seq.SeqPair. So it
    seems that Stack should inherit Seq.SeqPair or Seq.SeqEmpty and these
    unions begin to look like a lot of typing.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Greg Ewing@21:1/5 to Chris Angelico on Sat Nov 5 13:47:30 2022
    On 5/11/22 4:25 am, Chris Angelico wrote:
    Maybe it's one of those terms that is useless for actual coding
    (because practicality beats purity), but good for discussions?

    I'm not sure it's much good for discussions, either. I don't
    really care whether a language is "purely OO" or not, whatever
    that means, because I don't see purity of OO as a virtue.
    All I care about is what actual features then language has.

    --
    Greg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julieta Shem@21:1/5 to Stefan Ram on Sat Nov 5 13:41:14 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Julieta Shem <jshem@yaxenu.org> writes:
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    Later, I looked at a book in a bookstore; it was a book
    about programming by Barbara Liskov that came out after the
    LSP was already mentioned often, and as far as I could see,
    that book did not mention the LSP at all, although it
    actually had a chapter about subtypes!
    Do you remember the book's title?

    Unfortunately, no. I might have seen it between 1995 and 2010,
    so it could have been

    Program Development in Java:
    Abstraction, Specification, and Object-Oriented Design
    by John Guttag and Barbara Liskov,
    June 2000

    . It has this predecessor:

    Abstraction and Specification in Program Development
    by Barbara Liskov and John Guttag,
    March 1986

    . But I also found this paper, which I recommend to look at
    in this regard:

    A Behavioral Notion of Subtyping,
    Barbara Liskov and Jeannette M. Wing,
    1994

    . It contains the quite readable

    |Subtype Requirement: Let φ(z) 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.

    Thank you so much for the references!

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)