Hi Folks,<br><br>I am redesigning a system previously implemented in C++ using Haskell. I am keen to adopt a native programming style, but certain things about the original implementation still make perfect sense to me, including the C++-style subtype polymorphism. I'd be grateful if someone could take a look at the following sample and tell me if I'm missing some neater possible implementation in Haskell.<br>
<br><span style="font-family: courier new,monospace;">{-# OPTIONS -fglasgow-exts #-}</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">-- for existential types</span><br style="font-family: courier new,monospace;">
<br><span style="font-family: courier new,monospace;">-- all subclasses take the same input format and can be created factory-pattern style</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">data Input = Input Int Int<br></span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">-- this is the class we have to implement when creating a 'subtype'</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">class InternalAPI i where</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> doitImpl :: i -> Input -> Int</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> -- factory function, everything is initialised by giving an "i" and the Input</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> create :: i -> Input -> PublicAPI</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> create a b = PublicAPI a b<br style="font-family: courier new,monospace;">
</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">-- this is the object clients see, regardless of the underlying implementation</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">data PublicAPI = forall p. InternalAPI p => PublicAPI p Input</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">doit (PublicAPI p i) = doitImpl p i</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">-- Here's an example implementation of something trivial</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">data Adder = Adder</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">instance InternalAPI Adder where</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> doitImpl _ (Input a b) = a + b -- first argument is superfluous</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">-- There's the factory function in use:</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">test = create Adder (Input 1 2)</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">result = doit test</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;">I'm actually pretty happy with this. I'm not trying for a complete OOP implementation but it lets me:<br>
- have the 'base class' (InternalAPI) implement default versions of methods with full access to the 'Input' structure containing the member data<br>- do further inheritance, since I can select the implementation of a method using that first argument (e.g. on doitImpl)<br>
- base and derived classes see the same member data<br>- Implementations of 'InternalAPI' can reference each other through the PublicAPI interface.<br><br>Admittedly it's a rather vague question. I wonder if there are any articles out there showing how to reformulate problems solved using C++-style object models into Haskell programs? After all, it's hardly as if the C++ model is particularly elegant...<br>
<br>thanks,<br><br>David<br><br>