[Haskell-cafe] circular imports

Henning Thielemann schlepptop at henning-thielemann.de
Tue Sep 7 09:27:26 EDT 2010


Mathew de Detrich schrieb:

> I had the same issue zonks ago, and I resorted to using the hs-boot file
> method as well (which worked fine)
> 
> Which I guess brings me to my second point, is this something that GHC
> should do automatically when it sees circular dependencies? When I asked
> about it earlier on #haskell, I was told that its better that way
> because it discourages making bad design through
> circular dependencies (yet in my case and I assume the other cases as
> well, not using the hs-boot method would have made the design much
> worse). Are there any cases in particular where people would be
> encouraged to make interface design with circular dependencies (and that
> design be deemed as horrible) as opposed to what seems to be more
> realistic case where circular dependencies rarely crop up, and when they
> do they actually make the design better?

I like to compare it with Modula-3 where cyclic imports are strictly
forbidden. There it is solved by dividing a module into an interface
file and an implementation file. The interface can be compared with
.hs-boot files: You can define types there, but you can also simply
declare a type without its precise structure. Two mutually depending
types in distinct modules could be defined as follows:

A.i3:
INTERFACE A;
TYPE T <: REFANY; (* declare T to be a subtype of a general pointer *)
END A.

A.m3:
MODULE A; (* the implementation part automatically imports the
corresponding interface A without qualification *)
IMPORT B; (* import the interface of module B *)
REVEAL T = POINTER TO RECORD b : B.T END;
END A.


B.i3:
INTERFACE B;
TYPE T <: REFANY;
END B.

B.m3:
MODULE B;
IMPORT A;
REVEAL T = POINTER TO RECORD a : A.T END;
END B.


Thus the circular dependency graph
A -> B, B -> A
is split into the non-circular dependency graph
Ai -> Am, Ai -> Bm, Bi -> Am, Bi -> Bm

Obviously this only works for pointers, but this is no problem since
embedding a record in itself is not possible. Haskell cannot embed a
record in another one, it uses pointers by default.



More information about the Haskell-Cafe mailing list