
Benjamin Franksen wrote:
Timber (formerly O'Haskell) has gone this way. Its object model is defined (and in fact was implemented) by a straight-forward translation into a (state) reader monad transformer over the IO monad. It is noteworthy that in this translation the (local) state of a Timber object is not a record but just an (IORef to an) anonymous tuple. [It is true that they added 'real' records and subtyping to the language but these additions are completely orthogonal to the object model. Records are merely used to group the monadic actions that define the interface of an object into a suitable type hierarchy (in order to model a weak form of interface inheritance).]
Well without propper records you cannot do inheritance and other things relavent to the object model - so it is not orthogonal - but it is certainly true that you can define the state of an object using an IORef, and local scoping can provide the data-hiding necessary. We are using HLists to provide records and sub-typing, which are implemented using ghc's existing extensions to the class system (multi-parameter type and fundeps) - so no language extensions are necessary for these features - Haskell already has them, and they can be used quite reasonably from a library without syntax extensions.
So, one of the things I learned when studying Timber's object model is that records (or modules) with mutable fields (and inheritance and so on) are *not* the whole story. The most interesting aspect is how objects react to external stimulus, i.e. their representation as monadic effects.
Well the records implement method dictionaries, so they determine which inheritance and interface methods are possible.
One *can* program in such a way in Haskell. What's missing is not so much records or first class modules, nor top-level IO actions (safe or not), but suitable syntactic sugar to alleviate the burden of having to lift (sic!) all IO actions to a suitable object/context monad.
Erm no, all the objects can be implemented directly in the IO monad if you so wish, so no lifting is necessary... here is an example object in actuall Haskell code using the HList library...
point = do x <- newIORef 0 returnIO $ mutableX .=. x .*. getX .=. readIORef x .*. moveD .=. (\d -> modifyIORef x ((+) d)) .*. emptyRecord
And here's the object in use:
myFirstOOP = do p <- point p # getX >>= print p # moveD $ 3 p # getX >>= print
As you can see no lifting or awkwardness involved... the syntax looks very much like the OCaml example it was ported from. Admittedly a little syntactic sugar may make it more palatable to OO programers. (But notice how all the types for the objects methods are inferred by the compiler...) Keean.