[Haskell-cafe] Avoiding Dependency loops

martin martin.drautzburg at web.de
Sun Oct 25 11:29:46 UTC 2015


Am 10/25/2015 um 12:03 PM schrieb David Turner:
> Hi Martin,
> 
> Tempted to agree with you that Process has no business knowing about its runner - even in OO-land this would seem a bit
> unexpected to me. On the other hand, if PrcRunner is a function that takes a Process as its second argument, does a
> Process's prcRunner field ever run a Process other than the one that owns it? If not, it strikes me that partially
> applying it on construction be better: data PrcRunner = PrcRun (Timed Event -> State System EventQu)? 

This was where I was caught in OO-land. You are absolutely right and your answer helps a lot.


> Or you could
> decide to keep your types mutually recursive and define them all in their own .Types module and then your various
> implementation modules can depend on that without introducing any cycles.
> 
> Basically, there's lots of options depending on what you're trying to do! Drawing the boundaries around modules is
> definitely an art and not a science - there's a good (albeit OO-centric) article
> http://martinfowler.com/articles/refactoring-dependencies.html where Fowler says/"The hardest part of splitting a
> program into modules is just deciding on what the module boundaries should be. There's no easy guidelines to follow for
> this, indeed a major theme of my life's work is to try and understand what good module boundaries will look
> like." /which you may find interesting.
> 
> Cheers,
> 
> 
> On 25 October 2015 at 10:35, martin <martin.drautzburg at web.de <mailto:martin.drautzburg at web.de>> wrote:
> 
>     Thanks David.
> 
>     Maybe I better show the actual code;
> 
>     * module Process *
> 
>     data Process r = Prc {
>                 prcSources  :: [Port],
>                 prcSinks    :: [Port],
>                 prcRunner   :: r,
>                 prcId       :: PrcId
>     } deriving (Show)
> 
> 
>     * module System *
> 
>     data System = Sys {
>                 sysProcesses :: ProcessDb,
>                 sysItems     :: ItemDb
>     } deriving Show
> 
>     type ProcessDb = M.Map Int (Process PrcRunner)
> 
>     data PrcRunner = PrcRun (Timed Event -> Process PrcRunner -> State System EventQu)
> 
> 
>     I understand your reasoning about the type contraints, but I cannot see what I should actually do.
> 
>     AS for your second suggestion of splitting things up even more, I don't think this will help, because the loop is
>     already on type level.
> 
>     My feeling is that I got caught in OO thinking too much and the runner should not be part of the process at all. But
>     again I cannot see how to fix it. The thing is, Process itself does not really have any business with its Runner. There
>     is no function which actually does anyting with it. The Process type just provides a home for the runner.
> 
>     Thinking aloud:
> 
>     On top of the whole thing sits a simulation, which among others changes the state of the System. This includes changes
>     to the Processes. I want to capture this by altering the Runner associated with a particular Process. Wouldn't this
>     suggest that System needs an association between Process and Runner rather than making Runner a field of Process?
> 
> 
>     Am 10/25/2015 um 10:01 AM schrieb David Turner:
>     > Hi Martin,
>     >
>     > Seems reasonable to me. It's a common dependency-breaking technique, akin to introducing an interface in OO-land. Did
>     > you also introduce a typeclass constraint on the type 'r' so you can call some methods on it too? If not then Process
>     > doesn't really depend on System at all.
>     >
>     > Another thing to look for is that perhaps your System module splits into two bits, one low-level (defining types and so
>     > on) and one high-level (making use of everything in System and Process and Runner) and the two bits live at opposite
>     > ends of the dependency graph. I've found that quite a common situation to be in when splitting things up into modules too.
>     >
>     > HTH,
>     >
>     > David
>     >
>     >
>     >
>     >
>     > On 25 October 2015 at 08:36, martin <martin.drautzburg at web.de <mailto:martin.drautzburg at web.de> <mailto:martin.drautzburg at web.de
>     <mailto:martin.drautzburg at web.de>>> wrote:
>     >
>     >     Hello all,
>     >
>     >     I just split up a program I'm working on into several source files and ran into the following difficulty:
>     >
>     >     module Process implements Process which has a field Runner. Runner is a basically a function which alters a 'System'
>     >     state. So Process needs Runner, Runner needs System and thus Process needs System.
>     >
>     >     module System implements among others a collection of Processes (because Processes can alter their states). So System
>     >     needs Process.
>     >
>     >     Eh voila, I have a loop.
>     >
>     >     What I did was to leave the type of the Runner unspecified in Process, i.e. I now have (Process r) where r is the type
>     >     of the Runner. Thus Process no longer needs to know about System.
>     >
>     >     This does work, but it feels strange and I'm a bit worried that this is an indication for a design flaw, but I cannot
>     >     see it.
>     >     _______________________________________________
>     >     Haskell-Cafe mailing list
>     >     Haskell-Cafe at haskell.org <mailto:Haskell-Cafe at haskell.org> <mailto:Haskell-Cafe at haskell.org
>     <mailto:Haskell-Cafe at haskell.org>>
>     >     http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>     >
>     >
> 
> 



More information about the Haskell-Cafe mailing list