Pair('a', Pair('b', Pair('c', Empty())))Pair.fromIterable("abc")
'a'ls = Pair.fromIterable("abc")
ls.first
Pair('b', Pair('c', Empty()))ls = ls.rest
ls
Pair('a', Pair('b', Pair('c', Empty())))ls = Pair("a", ls)
ls
Stack(1, Empty())Stack(1, Empty())
1Stack(1, Empty()).pop()
Stack(2, Stack(1, Empty()))Stack(1, Empty()).push(2)
Traceback (most recent call last):Stack()
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"
Empty()stack()
Stack(4, Stack(3, Stack(2, Stack(1, Empty()))))stack(1,2,3,4)
. 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".
My desire seems to imply that I need a union-like data structure.
. 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''.
Empty()Stack()
Pair(1, Empty())Stack(1, Stack())
Stack()Stack()
Stack(1, Stack())Stack(1, Stack())
Stack('it', Stack())Stack().push("it")
Stack(3, Stack(2, Stack(1, Stack())))Stack(1, Stack()).push(2).push(3)
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().
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/
|2s=[]
s.append(1)
s.append(2)
s.pop()
|1s.pop()
|Traceback (most recent call last):s.pop()
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(...).
Pair('a', Pair('b', Pair('c', Empty())))Pair.fromIterable("abc")
'a'ls = Pair.fromIterable("abc")
ls.first
Pair('b', Pair('c', Empty()))ls = ls.rest
ls
Pair('a', Pair('b', Pair('c', Empty())))ls = Pair("a", ls)
ls
Stack(1, Empty())Stack(1, Empty())
1Stack(1, Empty()).pop()
Stack(2, Stack(1, Empty()))Stack(1, Empty()).push(2)
Traceback (most recent call last):Stack()
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.
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.
|2s=[]
s.append(1)
s.append(2)
s.pop()
|1s.pop()
|Traceback (most recent call last):s.pop()
| 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.
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.
I wrote the classes
class Empty:
...
class Pair:
...
(*) How to build a stack?
These Lisp-like sequences are clearly a stack.
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.
1Stack(1, Empty()).pop()
Stack(2, Stack(1, Empty()))Stack(1, Empty()).push(2)
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.
Traceback (most recent call last):Stack()
File "<stdin>", line 1, in <module>
TypeError: Pair.__init__() missing 2 required positional arguments: 'first' and 'rest'
class Pair:
def __init__(self, first=Empty(), rest=Empty()):
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)
To answer your question generally, Union’s are not OO.
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)
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.
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())))
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.
Stack()Stack()
Stack(1, Stack(2, Stack(3, Stack())))Stack(1, Stack(2, Stack(3, Stack())))
...Empty() # That's an empty stack
...Pair(1, Pair(2, Empty())) # That's a non-empty stack
Any book recomendations on getting this thing mathematics-clear?
The crucial feature OOP adds to this is polymorphism ("late binding").
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())))
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())))
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! [...]
But we've left behind a more basic requirement --- the Stack
class wishes for all the methods already written in some class called
Pair,
ram@zedat.fu-berlin.de (Stefan Ram) writes:
ram@zedat.fu-berlin.de (Stefan Ram) writes: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?
The crucial feature OOP adds to this is polymorphism ("late binding").If polymorphism is so crucial, the idea of subclasses
has something to it! [...]
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.)
I don't see any overlap between these at the conceptual level.
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.
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.
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.
To me what distinguishes functional from imperative is,
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
to complications. If in doubt use delegation instead.
What is delegation?
Any book recomendations on getting this thing mathematics-clear?
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!
... def f(self):class Language:
... def __init__(self):class English(Language):
... def __init__(self):class French(Language):
Good MorningEnglish().f()
BonjourFrench().f()
shows that in Python we do *not* need subclassing/inheritanceTo me, that is not really an example of polymorphism, but more an example of Python's "duck typing".
for polymorphism!
I'd implement the example hierarchy as
... def f(self):class Language:
... print(self.greeting)
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.)
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,To me, that is not really an example of polymorphism, but more an example of Python's "duck typing".
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!
I'd implement the example hierarchy as
... def f(self):class Language:
... print(self.greeting)
...
... def __init__(self):class English(Language):
... self.greeting = "Good Morning"
...
... def __init__(self):class French(Language):
... self.greeting = "Bonjour"
...
Good MorningEnglish().f()
BonjourFrench().f()
... with no explicit /function/ for greeting -- it's just an attribute set in each subtype, inheriting the "f" function for printing.
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.
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!
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").
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.
On 02/11/2022 20:21, Dennis Lee Bieber wrote:
But duck typing is a perfectly good implementation of polymorphism.shows that in Python we do *not* need subclassing/inheritanceTo me, that is not really an example of polymorphism, but more an
for polymorphism!
example of Python's "duck typing".
That's very interesting. Would you share the complete thread of e-mail?
I would love to read it word for word.
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.
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?
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".)
Thank you,
Stefan Ram
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
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.
|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.
3) ifTrue: <block of code>ifFalse: <block of code>
|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.
I'll definitely look up the Liskov
substitution principle and try to understand it.
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.
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.
Even an if/else test is a message to the boolean object:
3) ifTrue: <block of code>ifFalse: <block of code>
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?
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.)
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.
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!
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.
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.
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 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.
multidimensional thing that's so tangled up that it guarantees thatExactly so. During the 90's there came to be at least 3 different
people can happily debate for years to come.
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.
Class contracts must hold for subclasses.
In Python, everything is an object. Doesn't that equally mean that
Python is purely OOP?
(<joke>The empty documentation seems to satisfy the principle.</joke>)
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.
ram@zedat.fu-berlin.de (Stefan Ram) writes:
Later, I looked at a book in a bookstore; it was a bookDo you remember the book's title?
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!
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.
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!
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.
Maybe it's one of those terms that is useless for actual coding
(because practicality beats purity), but good for discussions?
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 bookDo you remember the book's title?
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!
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.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 448 |
Nodes: | 16 (2 / 14) |
Uptime: | 79:19:41 |
Calls: | 9,254 |
Calls today: | 6 |
Files: | 13,501 |
Messages: | 6,069,037 |