(1) Interfaces / typeclasses / traits are good; inheritance is bad. An object is a family of partially applied functions with some shared state; this means tight coupling between them, rather often unwanted.
(2) Not having mutable state is the point. The more state you have, and the more code paths can mutate the state, the harder it is to reason about the program correctly. Examples of various degrees of hilarity / severity abound.
Sometimes shared mutable state is inevitable for performance reasons. It should be well insulated from other parts of the program then. Same applies to other effects, such as I/O. The standard approach is a core of pure functions with an outer shell of effectful / stateful procedures (as opposed to a mix of them).
While I'm on this soapbox, let me remind that Smalltalk was initially envisioned as an actor system, something like Erlang (hence "messages"), but hardware limitations did not allow to implement the original vision.
That depends. Inheritance makes it easy to break encapsulation (which is bad -- agreed). It can be hard to model "Is-A"-Relationships properly, but I wouldn't call it inherently bad. A circle isn't an ellipsis; but a chair is furniture. The quality of code stems from your quality of thought.
> this means tight coupling between them, rather often unwanted.
That depends on your design. Coupling is the whole point, you do objects because you want to couple. Fraction.reduce, Fraction.add, Fraction.subtract, Fraction.multiply. Why not have them as as a cohesive unit, all these functions must understand the details of fraction anyway. Why not couple them?
> Not having mutable state is the point.
I agree. But objects never force you to publish their state; that's bad education. Getters and setters should be avoided. The Fraction above can be made immutable easily, Fraction(1, 2).add(Fraction(1,4)) --> <Fraction: 3/4>, leaving the old ones intact.
But then, it depends on how you communicate state.
I believe `Connection.Open()` should return an Object of Type `OpenConnection`. I believe that state changes should be communicated by changes of identity (either a new instance or a new Type), but ideas like this are most often answered by waving torches and pitchforks in front of my house at night (figuratively).
Your OpenConnection idea might make sense in some abstract way, but one thing I know about connections is that they have a habit of closing. They will close without any notice to your programming runtime, because the operating system will do it. What happens to your OpenConnection object then? Well it becomes invalidated, and now you have a nonsensical object hanging around. So you read from it, get an error, and... now what? Replace it with a ClosedConnection?
> What happens to your OpenConnection object then?
It raises an exception. This interrupts the normal flow of things and asks you to deal with the problem asap. If necessary, the handling code then could try to reconnect or abort. If desired, it could return a closed connection to convey that state-change, so that calling code is made aware that it needs to reconnect first and can't just reused the now closed connection. You could revert it to a normal connection (a "Has never ever been opened to begin with"-Connection). Depends on whether your driver/adapter/underlying connection thing cares about a difference in initial connects or reconnecting. If the handling code can't deal with it, it can bubble the exception up one level.
Swapping a `Connection` for an `OpenConnection` isn't heretic by the way, such structures are described by the state pattern. Objects model state, Exceptions model events (state transitions) but the later isn't explicitly described in the original Gang of Four book that way. I just found that exceptions are very usefull for this, given that you react to them in only a limited scope.
Be aware that this idea is culturally dependent. In Java, Exceptions are often discouraged from being used in such a way (exceptions should never be used for control flow and only convey error cases), in Python it's normal to communicate state transition that way, e.g. for-loops watch for StopIteration exceptions.
Inheritance is a common-sense solution to the problem of creating something similar to an existing object, but different in some key aspects. It's programming by difference. Nothing bad about it until you start using it for type information.
>An object is a family of partially applied functions with some shared state
This is not true because of dynamic dispatch and because in non-sucky languages objects can interpret messages.
Nothing stops you from doing OOP with state that is immutable once initialized though. Likewise, nothing stops language designers for enforcing that in their OOP languages. You are arguing against a strawman.
(2) Not having mutable state is the point. The more state you have, and the more code paths can mutate the state, the harder it is to reason about the program correctly. Examples of various degrees of hilarity / severity abound.
Sometimes shared mutable state is inevitable for performance reasons. It should be well insulated from other parts of the program then. Same applies to other effects, such as I/O. The standard approach is a core of pure functions with an outer shell of effectful / stateful procedures (as opposed to a mix of them).
While I'm on this soapbox, let me remind that Smalltalk was initially envisioned as an actor system, something like Erlang (hence "messages"), but hardware limitations did not allow to implement the original vision.