• Alan Kay on OOP

    From Salvador Mirzo@smirzo@example.com to comp.misc on Thu Feb 27 09:32:32 2025
    From Newsgroup: comp.misc

    If I recall correctly, the notion of OOP by Alan Kay was discussed not
    long ago in comp.lang.python. Stefan Ram shared an amazing e-mail reply
    given to him by Alan Kay himself. The e-mail was very enlightening, but
    I've spent quite some time on the last paragraph, which I'm still far
    from understanding.

    --8<-------------------------------------------------------->8---
    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

    [...]

    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
    --8<-------------------------------------------------------->8---

    I've read the Early History of Smalltalk, by the way. I think I
    understand what he means by ``messaging'', ``local retention and
    protection'' and ``hiding of state-process''. My knowledge reaches its
    limits a bit in ``extreme late-binding of all things'' and I get
    certainly puzzled in ``it can be done in Smalltalk and in LISP'' given
    that he is ``not aware of [others]''.

    I think he mentions late-binding because I suppose that is required for
    OOP in static languages like C++, say. But I would like to get a clear undertanding of what is early and late binding, so I probably should
    look at a text book with examples---homework for me to and I would be
    grateful if anyone wants to be my adviser here and point out a specific
    book or example.

    Now, to address my main puzzle. The email is from 2003. Alan Kay was certainly aware of Python, Java and so on. Why would his notion of OOP
    be impossible in Python or Java?

    My attempt at answering such question is this: Python, for example,
    doesn't enforce the hiding of state-process because there's no true
    private members in objects---on purpose. Is that a reason why Python
    fails to satisfy Alan Kay's OOP? So, his notion is that an object
    should really be completely opaque---the only way *possible* to interact
    with it would be to send it a message and possibly expect a returning
    message.

    In some lecture Alan Kay has given (to a class in a university in the
    US, whose name I can't remember), I remember he mentioned the Internet
    being a true OO system. That reinforced my idea above: a computer
    system cannot, for example, share state with one another in a direct way
    if their means of communication is a protocol such as IP.

    But I'm still puzzled. Although I'm sure LISP can do anything any other language ever could, it is also a language so hackable that any of its
    rules can also be violated by a LISP programmer, unless perhaps if we
    think of the programmer as the user of a certain interface whose
    implementation he cannot touch.

    Anyway, my objective here is to understand and be able to write examples
    in languages such as Common Lisp and Python to illustrate what he means
    by OOP. Thanks for any contribution anyone can offer here. Thanks!
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From ram@ram@zedat.fu-berlin.de (Stefan Ram) to comp.misc on Thu Feb 27 13:31:25 2025
    From Newsgroup: comp.misc

    Salvador Mirzo <smirzo@example.com> wrote or quoted:
    I've read the Early History of Smalltalk, by the way. I think I
    understand what he means by ``messaging'', ``local retention and
    protection'' and ``hiding of state-process''. My knowledge reaches its >limits a bit in ``extreme late-binding of all things'' and I get
    certainly puzzled in ``it can be done in Smalltalk and in LISP'' given
    that he is ``not aware of [others]''.

    Object-oriented programs contain expressions to describe the sending
    of messages at runtime. Here's an example of such an expression:

    o s: x

    . "o" is the receiver object, "s" the selector (keyword) of the
    message, and x is an argument.

    "Extreme late-binding of all things" to me (S. R.) means
    that it is possible to give the the values of "o" and "x"
    (including their types) only as late as immediately before
    "o s: x" is being evaluated (executed).

    Now, to address my main puzzle. The email is from 2003. Alan Kay was >certainly aware of Python, Java and so on. Why would his notion of OOP
    be impossible in Python or Java?

    He might have some criteria he has not mentioned.

    For example, in Smalltalk, blocks of code are objects, too,
    and you can define your own custom bool type and "if" statement
    using Smalltalk and such blocks. Maybe he requires that, in
    an object-oriented language, blocks of code are objects, too. [1]

    In LISP, you can have code values using "LAMBDA", and maybe
    that's the criterion Alan Kay used, as both Smalltalk and LISP
    have code block as run-time values.

    In Python and in Java there also are non-object-oriented features,
    and maybe he requires that, in an object-oriented programming
    language, everything has to be done in an object-oriented way;
    what might be called a "/pure(ly)/ object-oriented language".

    Maybe he requires metaclasses. Well, Python has them!

    [1]

    So, you define two classes "True" with

    ifTrue: aBlock
    ^aBlock value

    and "False" with

    ifTrue: aBlock
    ^nil

    . Now we have implemented an if statement using polymorphism
    that can be used as:

    a > 0 ifTrue: [a := 0]

    . I have not checked this code on a Smalltalk implementation.
    Maybe it needs some additions or modifications, but it gives
    the general idea.

    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!

    . I used "exec" to emulate block objects, one could also use
    "lambda" (but it is restricted in Python) or a form of "def".


    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From ram@ram@zedat.fu-berlin.de (Stefan Ram) to comp.misc on Thu Feb 27 14:11:24 2025
    From Newsgroup: comp.misc

    ram@zedat.fu-berlin.de (Stefan Ram) wrote or quoted:
    ifTrue: aBlock
    ^aBlock value

    and "False" with

    ifTrue: aBlock
    ^nil

    Heads up: All code of this post was generated and not tested!

    Here's an extended program, supossed to run unter GNU Smalltalk:

    Object subclass: Boolean [
    Boolean class >> new [
    self error: 'Boolean instances cannot be created'
    ]

    ifTrue: trueBlock [
    self subclassResponsibility
    ]

    ifFalse: falseBlock [
    self subclassResponsibility
    ]

    ifTrue: trueBlock ifFalse: falseBlock [
    self subclassResponsibility
    ]
    ]

    Boolean subclass: True [
    ifTrue: trueBlock [
    ^trueBlock value
    ]

    ifFalse: falseBlock [
    ^nil
    ]

    ifTrue: trueBlock ifFalse: falseBlock [
    ^trueBlock value
    ]
    ]

    Boolean subclass: False [
    ifTrue: trueBlock [
    ^nil
    ]

    ifFalse: falseBlock [
    ^falseBlock value
    ]

    ifTrue: trueBlock ifFalse: falseBlock [
    ^falseBlock value
    ]
    ]

    "Create global instances"
    true := True new.
    false := False new.

    "Example usage"
    a := -5.
    a < 0 ifTrue: [a := 0].
    a printNl.

    b := 10.
    b < 0 ifTrue: [b := 0].
    b printNl.

    (a = 0 and: [b = 10]) ifTrue: [
    'Both conditions are true' printNl
    ] ifFalse: [
    'At least one condition is false' printNl
    ].

    , expected output:

    0
    10
    Both conditions are true

    , Common Lisp,

    ;; Define the Boolean class (in Common Lisp, we'll use structures)
    (defstruct (boolean (:constructor nil)))

    ;; Define True and False subclasses
    (defstruct (true (:include boolean)))
    (defstruct (false (:include boolean)))

    ;; Create global instances
    (defparameter *true* (make-true))
    (defparameter *false* (make-false))

    ;; Define methods for True
    (defmethod if-true ((condition true) true-block)
    (funcall true-block))

    (defmethod if-false ((condition true) false-block)
    nil)

    (defmethod if-true-false ((condition true) true-block false-block)
    (funcall true-block))

    ;; Define methods for False
    (defmethod if-true ((condition false) true-block)
    nil)

    (defmethod if-false ((condition false) false-block)
    (funcall false-block))

    (defmethod if-true-false ((condition false) true-block false-block)
    (funcall false-block))

    ;; Helper function to convert boolean to our custom boolean objects
    (defun to-boolean (value)
    (if value *true* *false*))

    ;; Example usage
    (let ((a -5))
    (if-true (to-boolean (< a 0))
    (lambda () (setf a 0)))
    (print a))

    (let ((b 10))
    (if-true (to-boolean (< b 0))
    (lambda () (setf b 0)))
    (print b))

    (if-true-false (to-boolean (and (= a 0) (= b 10)))
    (lambda () (print "Both conditions are true"))
    (lambda () (print "At least one condition is false")))

    , Python,

    from abc import ABC, abstractmethod

    class Boolean(ABC):
    @abstractmethod
    def if_true(self, block):
    pass

    @abstractmethod
    def if_false(self, block):
    pass

    @abstractmethod
    def if_true_if_false(self, true_block, false_block):
    pass

    class True(Boolean):
    def if_true(self, block):
    return block()

    def if_false(self, block):
    return None

    def if_true_if_false(self, true_block, false_block):
    return true_block()

    class False(Boolean):
    def if_true(self, block):
    return None

    def if_false(self, block):
    return block()

    def if_true_if_false(self, true_block, false_block):
    return false_block()

    # Create global instances
    true = True()
    false = False()

    # Example usage
    a = -5
    (true if a < 0 else false).if_true(lambda: globals().update(a=0))
    print(a)

    b = 10
    (true if b < 0 else false).if_true(lambda: globals().update(b=0))
    print(b)

    (true if a == 0 and b == 10 else false).if_true_if_false(
    lambda: print("Both conditions are true"),
    lambda: print("At least one condition is false")
    )

    , Java,

    import java.util.function.Supplier;

    abstract class Boolean {
    abstract <T> T ifTrue(Supplier<T> trueBlock);
    abstract <T> T ifFalse(Supplier<T> falseBlock);
    abstract <T> T ifTrueIfFalse(Supplier<T> trueBlock, Supplier<T> falseBlock);
    }

    class True extends Boolean {
    @Override
    <T> T ifTrue(Supplier<T> trueBlock) {
    return trueBlock.get();
    }

    @Override
    <T> T ifFalse(Supplier<T> falseBlock) {
    return null;
    }

    @Override
    <T> T ifTrueIfFalse(Supplier<T> trueBlock, Supplier<T> falseBlock) {
    return trueBlock.get();
    }
    }

    class False extends Boolean {
    @Override
    <T> T ifTrue(Supplier<T> trueBlock) {
    return null;
    }

    @Override
    <T> T ifFalse(Supplier<T> falseBlock) {
    return falseBlock.get();
    }

    @Override
    <T> T ifTrueIfFalse(Supplier<T> trueBlock, Supplier<T> falseBlock) {
    return falseBlock.get();
    }
    }

    public class SmalltalkToJava {
    private static final Boolean TRUE = new True();
    private static final Boolean FALSE = new False();

    public static void main(String[] args) {
    int[] a = {-5};
    (a[0] < 0 ? TRUE : FALSE).ifTrue(() -> {
    a[0] = 0;
    return null;
    });
    System.out.println(a[0]);

    int[] b = {10};
    (b[0] < 0 ? TRUE : FALSE).ifTrue(() -> {
    b[0] = 0;
    return null;
    });
    System.out.println(b[0]);

    (a[0] == 0 && b[0] == 10 ? TRUE : FALSE).ifTrueIfFalse(
    () -> {
    System.out.println("Both conditions are true");
    return null;
    },
    () -> {
    System.out.println("At least one condition is false");
    return null;
    }
    );
    }
    }

    , C++,

    #include <iostream>
    #include <functional>

    class Boolean {
    public:
    virtual void ifTrue(const std::function<void()>& block) = 0;
    virtual void ifFalse(const std::function<void()>& block) = 0;
    virtual void ifTrue(const std::function<void()>& trueBlock, const std::function<void()>& falseBlock) = 0;
    };

    class True : public Boolean {
    public:
    void ifTrue(const std::function<void()>& block) override {
    block();
    }

    void ifFalse(const std::function<void()>& block) override {}

    void ifTrue(const std::function<void()>& trueBlock, const std::function<void()>& falseBlock) override {
    trueBlock();
    }
    };

    class False : public Boolean {
    public:
    void ifTrue(const std::function<void()>& block) override {}

    void ifFalse(const std::function<void()>& block) override {
    block();
    }

    void ifTrue(const std::function<void()>& trueBlock, const std::function<void()>& falseBlock) override {
    falseBlock();
    }
    };

    int main() {
    True true_obj;
    False false_obj;

    int a = -5;
    (a < 0 ? true_obj : false_obj).ifTrue([&]() { a = 0; });
    std::cout << a << std::endl;

    int b = 10;
    (b < 0 ? true_obj : false_obj).ifTrue([&]() { b = 0; });
    std::cout << b << std::endl;

    (a == 0 && b == 10 ? true_obj : false_obj).ifTrue(
    []() { std::cout << "Both conditions are true" << std::endl; },
    []() { std::cout << "At least one condition is false" << std::endl; }
    );

    return 0;
    }

    , C,

    #include <stdio.h>
    #include <stdbool.h>

    typedef struct Boolean Boolean;
    typedef struct True True;
    typedef struct False False;

    struct Boolean {
    void (*ifTrue)(Boolean* self, void (*block)(void));
    void (*ifFalse)(Boolean* self, void (*block)(void));
    void (*ifTrueIfFalse)(Boolean* self, void (*trueBlock)(void), void (*falseBlock)(void));
    };

    struct True {
    Boolean base;
    };

    struct False {
    Boolean base;
    };

    void True_ifTrue(Boolean* self, void (*block)(void)) {
    block();
    }

    void True_ifFalse(Boolean* self, void (*block)(void)) {
    // Do nothing
    }

    void True_ifTrueIfFalse(Boolean* self, void (*trueBlock)(void), void (*falseBlock)(void)) {
    trueBlock();
    }

    void False_ifTrue(Boolean* self, void (*block)(void)) {
    // Do nothing
    }

    void False_ifFalse(Boolean* self, void (*block)(void)) {
    block();
    }

    void False_ifTrueIfFalse(Boolean* self, void (*trueBlock)(void), void (*falseBlock)(void)) {
    falseBlock();
    }

    True true_obj = {{True_ifTrue, True_ifFalse, True_ifTrueIfFalse}};
    False false_obj = {{False_ifTrue, False_ifFalse, False_ifTrueIfFalse}};

    #define TRUE ((Boolean*)&true_obj)
    #define FALSE ((Boolean*)&false_obj)

    int main() {
    int a = -5;
    (a < 0 ? TRUE : FALSE)->ifTrue(^{
    a = 0;
    });
    printf("%d\n", a);

    int b = 10;
    (b < 0 ? TRUE : FALSE)->ifTrue(^{
    b = 0;
    });
    printf("%d\n", b);

    (a == 0 && b == 10 ? TRUE : FALSE)->ifTrueIfFalse(
    ^{ printf("Both conditions are true\n"); },
    ^{ printf("At least one condition is false\n"); }
    );

    return 0;
    }

    .


    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.misc on Thu Feb 27 21:24:35 2025
    From Newsgroup: comp.misc

    On 27 Feb 2025 13:31:25 GMT, Stefan Ram wrote:

    Maybe he requires metaclasses. Well, Python has them!

    Is there another language that does?

    (Smalltalk doesn’t count.)
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.misc on Thu Feb 27 23:43:14 2025
    From Newsgroup: comp.misc

    On Thu, 27 Feb 2025 09:32:32 -0300, Salvador Mirzo wrote:

    I think he mentions late-binding because I suppose that is required for
    OOP in static languages like C++, say.

    Consider a simple inheritance relationship like (Python example):

    class ErrorReturn(Exception) :
    ...

    def to_message(self, validate = None) :
    ...
    #end to_message

    #end ErrorReturn

    class InterfaceErrorReturn(ErrorReturn) :
    ...

    def to_message(self) :
    return super().to_message(self.validate)
    #end to_message

    #end InterfaceErrorReturn

    If you call to_message() on an instance of InterfaceErrorReturn, you will
    be invoking the method defined in that class, not in the ErrorReturn superclass. This happens even in a context where the code might be
    expecting an instance of ErrorReturn. That’s “late binding”, where the method dispatch depends on the dynamic type of the object, not on some expected type that might be deduced by the compiler from the context.

    In C++, you have to get this behaviour by prefixing your method
    definitions with the “virtual” keyword. You even have to do this for the destructor (if any), even though non-“virtual” destructors don’t make sense.

    In more pure OO languages, all methods are effectively “virtual”, so no special keyword is needed to indicate this.

    Now, to address my main puzzle. The email is from 2003. Alan Kay was certainly aware of Python, Java and so on. Why would his notion of OOP
    be impossible in Python or Java?

    I’m sure he was aware of Java, but possibly not Python. Probably he was aware of Dylan. He might even have considered Python beneath his notice:
    at that time it was still in version 2.3 or so, and was still carrying
    around the baggage of two different ways of defining classes: the old way
    and the right way.
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Salvador Mirzo@smirzo@example.com to comp.misc on Mon Mar 17 20:42:26 2025
    From Newsgroup: comp.misc

    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Salvador Mirzo <smirzo@example.com> wrote or quoted:
    I've read the Early History of Smalltalk, by the way. I think I
    understand what he means by ``messaging'', ``local retention and >>protection'' and ``hiding of state-process''. My knowledge reaches its >>limits a bit in ``extreme late-binding of all things'' and I get
    certainly puzzled in ``it can be done in Smalltalk and in LISP'' given
    that he is ``not aware of [others]''.

    Object-oriented programs contain expressions to describe the sending
    of messages at runtime. Here's an example of such an expression:

    o s: x

    . "o" is the receiver object, "s" the selector (keyword) of the
    message, and x is an argument.

    "Extreme late-binding of all things" to me (S. R.) means
    that it is possible to give the the values of "o" and "x"
    (including their types) only as late as immediately before
    "o s: x" is being evaluated (executed).

    Thanks. That makes sense!

    Now, to address my main puzzle. The email is from 2003. Alan Kay was >>certainly aware of Python, Java and so on. Why would his notion of OOP
    be impossible in Python or Java?

    He might have some criteria he has not mentioned.

    Okay. I thought there was something obvious to some that I didn't know.

    For example, in Smalltalk, blocks of code are objects, too,
    and you can define your own custom bool type and "if" statement
    using Smalltalk and such blocks. Maybe he requires that, in
    an object-oriented language, blocks of code are objects, too. [1]

    Interesting. What is the class of these objects that are blocks of
    code? (Thanks for the awesome example in [1].)

    In LISP, you can have code values using "LAMBDA", and maybe
    that's the criterion Alan Kay used, as both Smalltalk and LISP
    have code block as run-time values.

    In Python and in Java there also are non-object-oriented features,
    and maybe he requires that, in an object-oriented programming
    language, everything has to be done in an object-oriented way;
    what might be called a "/pure(ly)/ object-oriented language".

    Yeah---that makes sense to me. Maybe he requires that really everything
    must be an object, including code blocks. So I guess Smalltalk and LISP
    are the purely object-oriented languages---or at least the first ones.

    [1]

    So, you define two classes "True" with

    ifTrue: aBlock
    ^aBlock value

    and "False" with

    ifTrue: aBlock
    ^nil

    . Now we have implemented an if statement using polymorphism
    that can be used as:

    a > 0 ifTrue: [a := 0]

    . I have not checked this code on a Smalltalk implementation.
    Maybe it needs some additions or modifications, but it gives
    the general idea.

    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!

    Very cool! Thanks for the example!
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From ram@ram@zedat.fu-berlin.de (Stefan Ram) to comp.misc on Tue Mar 18 09:57:19 2025
    From Newsgroup: comp.misc

    Salvador Mirzo <smirzo@example.com> wrote or quoted:
    Interesting. What is the class of these objects that are blocks of
    code? (Thanks for the awesome example in [1].)

    In Smalltalk-80 (by the Blue Book) it might be "BlockContext", see:
    "Smalltalk-80, The Language and its Implementation" (1983) by Adele
    Goldberg and David Robson, Part Fourt, Chapter "The Implementation",
    Section "The Interpreter", side heading "Block Contexts":

    |A "BlockContext" represents a block

    . Is "BlockContext" a class? In

    |Class BlockContext

    in Part Fourt, Chapter "The Implementation", Section "Objects
    used by the Interpreter", side heading "Contexts", routine
    "initializeContextlndices", line 8 it seems to be called a class.

    However, since this detail is part of the implementation, some
    Smalltalk implementations might use a different class for blocks!


    --- Synchronet 3.20c-Linux NewsLink 1.2