Since you have done a significant amount of work with Haskell, I must presume you have a solid understanding of monads. If so, I can suggest reading
It is "for the OO programmer", but it does a good job of clarifying some issues.
In particular, a "class hierarchy" is analogous to a stack of monads (made with monad transformers). That is to say, a class attaches 'methods' to an 'object' (i.e., a context, which might include data). Different languages use different monads to attach methods to objects -- for example, JavaScript uses "prototype-based" object orientation, which is very much analogous to the Haskell pattern:
data SomeData = ...
defaultData :: SomeData
defaultData = ...
setName :: Name -> SomeData -> SomeData
setName = ...
namedData :: SomeData
namedData = setName (Name "Pete") defaultData
Typically, an object oriented language provides an enriched IO monad which provides method dispatch based on context, state "for free" by setting variables (i.e., the dreaded side-effect), and so on. From a functional perspective, this is all syntactic sugar for a complicated monad transformer stack:
type OO a = ClassT a (StateT a IO) a
Typical object oriented languages are procedural in nature, and use explicit looping to handle iterating over data structures. Typically, all work is done in the same monad and it is "impossible" to define your own monads using "best practices" for that OO language.