[Haskell-cafe] Avoiding Dependency loops

David Turner dct25-561bs at mythic-beasts.com
Sun Oct 25 11:03:04 UTC 2015


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)? 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> 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>> 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>
> >     http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> >
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20151025/2b34da90/attachment.html>


More information about the Haskell-Cafe mailing list