Hmm, I had thought that Python didn't allow you to add data attributes to objects derived from immutable classes like `tuple` or `str`, but evidently I was wrong about that!

>>> class S(str): p = lambda me: len(me) + 3
...
>>> S("hi").p()
5
>>> S("uhoh").z = 3
>>>

You can't even detect this by enumerating the object's attributes with `dir()` or `.__dict__` at some moment — a method, or external code, can add new attributes at any later moment.

(Hypothetically in CPython maybe you could disassemble the bytecode of a method to see if it tries to mutate the object or some global variable, but even if this is possible, you can also use cffi to change the value of 3 in CPython. I regard both of these as breaking the semantics of Python per se.)

So, I was wrong about Python. And, on further reflection, I was wrong about C++ as well, because even if you provide a `const`-argument override for a function, which is a thing you can do to observe whether an object is `const` or not from the perspective of your caller:

int x(int* foo) { return *foo + 3; }
int x(const int* foo) { return *foo + 2; }
// declaring as `const int i` changes program behavior:
int main(int argc, char **argv) { int i = 7; return x(&i); }

there's not even a compiler warning if your caller fails to do so as well:

int x(int* foo) { return *foo + 3; }
int x(const int* foo) { return *foo + 2; }
// int y(int* foo) { return x(foo); }
int y(const int* foo) { return x(foo); }
int main(int argc, char **argv) { int i = 7; return y(&i); }

D's `immutable` is externally observable in the same way as `const` in D or in C++, but rigorously so in the sense we want here: invoking a function whose parameter is `immutable` with a mutable object is a compile-time error.

Also, D's `immutable` is transitive, unlike C++'s `const`, but like, for example, E's DeepFrozen.

In the simple object calculus, everything is an object and there is no mutability at all. You could reasonably object (heh) that, although it's intended to capture object-oriented programming, and Abadí and Cardelli's book is called A Theory of Objects, it's not widely used, so opinions may differ as to whether they succeeded. https://news.ycombinator.com/item?id=26588642 goes into more detail there.

So your definition of "object" only excludes each of the different concepts that D and Abadí and Cardelli's object-calculus call "objects" (and I guess I should add E), not Python's or C++'s as well.

As for Kotlin, my claim about Kotlin was not about Kotlin "objects" but about Kotlin "functions", which are closures that can include access to mutable state, just as in Scheme and OCaml. I don't know Kotlin well enough to know whether your definition of "object" fits its definition of "object". It's an interesting question. Maybe it does! Kotlin's data classes, for example, can contain mutable attributes, and there really is no (non-reflective) way to observe them from another class — for example, for a method to refuse to accept an argument if it happens to be mutable in this way. Kotlin/Native evidently has a form of transitive and externally observable immutability, but I don't know much about it.

Your revision of the definition to include a bundle of method handles does seem to neatly exclude what are called "functions" in Scheme, OCaml, and Kotlin. I think it might also exclude objects in E, but since E is dynamically typed, that's sort of an implementation detail, and anyway E doesn't figure prominently in the consensus definition of the term "object".

Thank you for a very educational and stimulating discussion!