Does Clojure solve this to a satisfying degree? One gives up static typing to embrace Clojure’s solutions.

On the other hand, Rust / Haskell provide partial solutions with static typing — but run into annoying boilerplate related to the orphan rule.

I’m not sure if there’s a true solution, given the level of thinking which has gone into these two forks in the road.

Maybe Haskell has some solution .. but it seems just an inherent problem with static typing. It's mostly a matter of boilerplate in a sense.

In Clojure, if in your application you extend a record (say from some library) and implement a new protocol, you've effectively implicitly created a new extended record type. In Clojure that happens transparently b/c its dynamic. In the static typing scenario you have to explicitly create this "extended type" that has the parent type and also implements the new protocol. You'd then explicitly use ExtendedRecordA. It can still be used where the parent was expected.

That's more verbose but maybe manageable.. Now if you have multiple libraries extending one record with different protocols .. and then you have a library/application where you included all these libraries. You want to use all these protocols from that one record.. how do you not do that? you'd need some aggregation step to make ExtendedRecordA and ExtendedRecordB and ExtendedRecordC all come together under some new super-type that has all the protocols. It's not impossible but it's just messier

I was also confused by the claim that Clojure solves it and Haskell does not.

> run into annoying boilerplate related to the orphan rule

I always forget this, but it's not an orphan if you put the instance in the same module as the class definition (as opposed to bundling it with a newtype elsewhere). Class definitions and instances would go in Evaluatable and Stringable.