Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I remove this type of mutual recursion?

I am running into a mutual recursion issue. The basic structure I have employed is that I have a module that defines a type class and several modules that define instances of that type class. Each instance however is defined in terms of all the other instances.

If that description is a little too abstract here is some code that has a structure like my code. (I've trimmed it down quite a bit to make the necessary bits obvious and added some ellipses to parts that are not relevant to the overall structure).

My class looks like the following:

data Result = ...

class Foo a where
  openFoo  :: Result      -> IO (a, Result)
  runFoo   :: (a, Result) -> IO (a, Result)
  closeFoo :: (a, Result) -> IO Result

Then I have the instances

data XData = ...

instance Foo XData where
   openFoo result = ...
   runFoo (data, result) = do
     currentEvent <- getEvent
     case currentEvent of
       EventA -> return (data, result)
       EventB ->
         (openFoo result :: IO YData)
           >>= runFoo
             >>= closeFoo
               >>= curry return data
   closeFoo (data, result) = ...

data YData = ...

instance Foo YData where
   openFoo result = ...
   runFoo (data, result) = do
     currentEvent <- getEvent
     case currentEvent of
       EventA -> return (data, result)
       EventB ->
         (openFoo result :: IO XData)
           >>= runFoo
             >>= closeFoo
               >>= curry return data
   closeFoo (data, result) = ...

Now I could simply resolve this by putting all of my instances into a single module, however rather than the 2 shown in my example I have 8 instances that are all mutually recursive with each other. On top of that each instance is quite large. Meaning the resulting module would be an enormous innavigable mess.

Now the haskell wiki has two suggestion for solving mutual recursion issues, but both of them are really more about mutually recursive types and neither of them is going to work here.

Is there anyway to get around this mutual recursion without simply combining all of my modules?

like image 206
Wheat Wizard Avatar asked Mar 08 '19 20:03

Wheat Wizard


1 Answers

Here's one slightly hacky way to do it. First, put your recursive definitions in one module:

module Internal.Recursive

data XData = ...
data YData = ...

-- Recursive definitions...

Then re-export each definition from a separate module:

module XData (IR.XData) where

import qualified Internal.Recursive as IR

module YData (IR.XYata) where

import qualified Internal.Recursive as IR

This will give the appearance of mutually recursive modules. (I don't believe that GHC allows any easy way of making actual recursive modules.)

like image 120
bradrn Avatar answered Sep 22 '22 17:09

bradrn