From 60bbba360c8ed5267f10e6b599b8884b6bc2e4ce Mon Sep 17 00:00:00 2001 From: simonpj Date: Tue, 1 Feb 2005 17:27:34 +0000 Subject: [PATCH] [project @ 2005-02-01 17:27:34 by simonpj] First cut at hs-boot documentation --- ghc/docs/users_guide/separate_compilation.xml | 363 +++++++++++++------------ 1 file changed, 187 insertions(+), 176 deletions(-) diff --git a/ghc/docs/users_guide/separate_compilation.xml b/ghc/docs/users_guide/separate_compilation.xml index 9a6b7e3..cb80aad 100644 --- a/ghc/docs/users_guide/separate_compilation.xml +++ b/ghc/docs/users_guide/separate_compilation.xml @@ -612,6 +612,151 @@ $ ghc -c parse/Foo.hs parse/Bar.hs gurgle/Bumble.hs -odir `arch` + + How to compile mutually recursive modules + + module system, recursion + recursion, between modules + + GHC supports the compilation of mutually recursive modules. + This section explains how. + + Every cycle in the module import graph must be broken by a hs-boot file. + Suppose that modules A.hs and B.hs are Haskell source files, + thus: + +module A where + import B( TB(..) ) + + newtype TA = MkTA Int + + f :: TB -> TA + f (MkTB x) = MkTA x + +module B where + import {-# SOURCE #-} A( TA(..) ) + + data TB = MkTB !Int + + g :: TA -> TB + g (MkTA x) = MkTB x + +hs-boot + files importing, + hi-boot files +Here A imports B, but B imports +A with a {-# SOURCE #-} pragma, which breaks the +circular dependency. For every module A.hs that is {-# SOURCE #-}-imported +in this way there must exist a souce file A.hs-boot. This file contains an abbreviated +version of A.hs, thus: + +module A where + newtype TA = MkTA Int + +A hs-boot file is compiled by GHC, just like a hs file: + + ghc -c A.hs-boot + +Just as compiling A.hs produces an +interface file A.hi, and an object file +A.o, so compiling A.hs-boot +produces an interface file +A.hi-boot, and an pseudo-object file +A.o-boot. The interface file +A.hi-boot has exactly the same format as any +other interface file. The pseudo-object file is empty (don't link it!), but it is +very useful when using a Makefile, to record when the A.hi-boot was +last brought up to date. + +To compile these three files, issue the following commands: + + ghc -c A.hs-boot -- Poduces A.hi-boot, A.o-boot + ghc -c B.hs -- Consumes A.hi-boot, produces B.hi, B.o + ghc -c A.hs -- Consumes B.hi, produces A.hi, A.o + ghc -o foo A.o B.o -- Linking the program + + +There are several points to note here: + + + The file A.hs-boot is a programmer-written source file. + It must live in the same directory as its parent source file A.hs. + (Currently, if you use a literate source file A.lhs you must + also use a literate boot file, A.lhs-boot.) + + + The hi-boot generated by compiling a hs-boot + file is in machine-generated binary format. + You can display its contents with ghc --show-iface. If you + specify a directory for interface files, the flag, then that affects + hi-boot files too.b + + Hs-boot files are written in a subset of Haskell. In particular, the module + exports and imports, and the scoping rules are exactly the same as in Haskell. Hence, to + mention a non-Prelude type or class, you must import it. + + When a hs-boot file A.hs-boot + is compiled, it is checked for scope and type errors. + When its parent module A.hs is compiled, the two are compared, and + an error is reported if the two are inconsistent. + + + If hs-boot files are considered distinct from their parent source + files, and if a {-# SOURCE #-} import is considered to refer to the + hs-boot file, then the module import graph must have no cycles. The ghc -M + will report an error if a cycle is found. + + + + +A hs-boot file need only contain the bare + minimum of information needed to get the bootstrapping process + started. For example, it doesn't need to contain declarations + for everything that module + A exports, only the things required by the + module that imports A recursively. +A hs-boot file is written in a subset of Haskell: + + The module header, and import statements, are exactly as in Haskell. + There must be no value declarations, but there can be type signatures for +values. For example: + + double :: Int -> Int + + + Fixity declarations are exactly as in Haskell. + Type synonym declarations are exactly as in Haskell. + A data type declaration can either be given in full, exactly as in Haskell, or it +can be given abstractly, by omitting the '=' sign and everything that follows. For example: + + data T a b + + In a source program + this would declare TA to have no constructors (a GHC extension: see ), + but in an hi-boot file it means "I don't know or care what the constructors are". + This is the most common form of data type declaration, because it's easy to get right. + You can also write out the constructors but, if you do so, you must write + it out precisely as in its real definition. + + If you do not write out the constructors, you may need to give a kind + annotation (), to tell + GHC the kind of the type variable, if it is not "*". (In source files, this is worked out + from the way the type variable is used in the constructors.) For example: + + data R (x :: * -> *) y + + + Class declarations is exactly as in Haskell, except that you may not put +default method declarations. You can also omit all the class methods entirely. + + Do not include instance declarations. There is a complication to do with +how the dictionary functions are named. It may well work, but it's not a well-tested feature. + + + + + + Using <command>make</command> @@ -644,6 +789,15 @@ cool_pgm : $(OBJS) .hs.o: $(HC) -c $< $(HC_OPTS) +.o-boot.hi-boot: + @: + +.lhs-boot.o-boot: + $(HC) -c $< $(HC_OPTS) + +.hs-boot.o-boot: + $(HC) -c $< $(HC_OPTS) + # Inter-module dependencies Foo.o Foo.hc Foo.s : Baz.hi # Foo imports Baz Main.o Main.hc Main.s : Foo.hi Baz.hi # Main imports Foo and Baz @@ -667,6 +821,9 @@ Main.o Main.hc Main.s : Foo.hi Baz.hi # Main imports Foo and Baz on the source. The rule says a .hi file can be made from a .o file by doing…nothing. Which is true. + Note that the suffix rules are all repeated twice, once + for normal Haskell source files, and once for hs-boot + files (see ). Note the inter-module dependencies at the end of the Makefile, which take the form @@ -681,9 +838,12 @@ Foo.o Foo.hc Foo.s : Baz.hi # Foo imports Baz Baz.hi, then the out-of-date file must be brought up to date. To bring it up to date, make looks for a rule to do so; one of the - preceding suffix rules does the job nicely. + preceding suffix rules does the job nicely. These dependencies + can be generated automatically by ghc; see + - + + Dependency generation dependencies in Makefiles Makefile dependencies @@ -702,42 +862,41 @@ depend : Now, before you start compiling, and any time you change the imports in your program, do make depend before you do make - cool_pgm. ghc -M will + cool_pgm. The command ghc -M will append the needed dependencies to your Makefile. - In general, if module A contains the - line - + In general, ghc -M Foo does the following. + For each module M in the set + Foo plus all its imports (transitively), + it adds to the Makefile: + + A line recording the dependence of the object file on the source file. -import B ...blah... +M.o : M.hs - - then ghc -M will generate a dependency line - of the form: - +(or M.lhs if that is the filename you used). + + For each import declaration import X in M, + a line recording the dependence of M on X: -A.o : B.hi - - - If module A contains the line - - -import {-# SOURCE #-} B ...blah... - - - then ghc -M will generate a dependency - line of the form: - +M.o : X.hi + + For each import declaration import {-# SOURCE #-} X in M, + a line recording the dependence of M on X: -A.o : B.hi-boot +M.o : X.hi-boot - (See for details of - hi-boot style interface files.) If - A imports multiple modules, then there will - be multiple lines with A.o as the + hi-boot style interface files.) + + + If M imports multiple modules, then there will + be multiple lines with M.o as the target. + There is no need to list all of the source files as arguments to the ghc -M command; + ghc traces the dependencies, just like ghc --make + (a new feature in GHC 6.4). By default, ghc -M generates all the dependencies, and then concatenates them onto the end of @@ -878,156 +1037,8 @@ ghc -M -optdep-f -optdep.depend ... - - - How to compile mutually recursive modules - - module system, recursion - recursion, between modules - - Currently, the compiler does not have proper support for - dealing with mutually recursive modules: - - -module A where - -import B - -newtype TA = MkTA Int - -f :: TB -> TA -f (MkTB x) = MkTA x --------- -module B where - -import A - -data TB = MkTB !Int - -g :: TA -> TB -g (MkTA x) = MkTB x - - - When compiling either module A and B, the compiler will - try (in vain) to look for the interface file of the other. So, - to get mutually recursive modules off the ground, you need to - hand write an interface file for A or B, so as to break the - loop. These hand-written interface files are called - hi-boot files, and are placed in a file - called <module>.hi-boot. To import - from an hi-boot file instead of the standard - .hi file, use the following syntax in the - importing module: hi-boot - files importing, - hi-boot files - - -import {-# SOURCE #-} A - - - The hand-written interface need only contain the bare - minimum of information needed to get the bootstrapping process - started. For example, it doesn't need to contain declarations - for everything that module - A exports, only the things required by the - module that imports A recursively. - - For the example at hand, the boot interface file for A - would look like the following: - - -module A where -newtype TA = MkTA GHC.Base.Int - - - Notice that we only put the declaration for the newtype - TA in the hi-boot file, - not the signature for f, since - f isn't used by B. - - The syntax is similar to a normal Haskell source file, but - with some important differences: - - - - Local entities (ones defined in the same hi-boot file may - be mentioned unqualified, but non-local entities (ones defined in other modules) - must be qualified with their - original defining module. Qualifying - by a module which just re-exports the entity won't do. In - particular, most Prelude entities aren't - actually defined in the Prelude (see for - example GHC.Base.Int in the above - example). HINT: to find out the fully-qualified name for - entities in the Prelude (or anywhere for - that matter), try using GHCi's - :info command, eg. -Prelude> :m -Prelude -> :i IO.IO --- GHC.IOBase.IO is a type constructor -newtype GHC.IOBase.IO a -... - - - Only data, type, - newtype, class, and - type signature declarations may be included. You cannot declare - instances or derive them automatically with - a deriving clause. - - - For data or newtype declaration, you may omit all -the constructors, by omitting the '=' and everything that follows it: - -module A where - data TA - - In a source program - this would declare TA to have no constructors (a GHC extension: see ), - but in an hi-boot file it means "I don't know or care what the constructors are". - This is the most common form of data type declaration, because it's easy to get right. - - You can also write out the constructors but, if you do so, you must write - it out precisely as in its real definition. - It is especially delicate if you use a strictness annotation "!", - with or without an {-# UNPACK #-} pragma. In a source file - GHC may or may not choose to unbox the argument, but in an hi-boot file it's - assumed that you express the outcome of this decision. - (So in the cases where GHC decided not to unpack, you must not use the pragma.) - Tread with care. - - Regardless of whether you write the constructors, you must write all the type parameters, - including their kinds - if they are not '*'. (You can give explicit kinds in source files too (), - but you must do so in hi-boot files.) - - - - In a class declararation, you may not specify any class operations; that is, - there can be no where part. If you want to use the class operations in a recursive - way, declare them in the hi-boot file with separate, overloaded type signatures, thus: - - class Num a - (+) :: Num a => a -> a -> a - - - - - -If M.hi-boot mentions an entity N.f, defined in some other -module N, then GHC will by default go hunting for N.hi. If module -N is not yet compiled either, GHC won't look for N.hi-boot; it'll just -complain. To fix this, in the source file that uses -import {-# SOURCE #-} M, add -import {-# SOURCE #-} N(). (The "()" says that you don't want to import anything into -your current scope, and will prevent unused-import warnings.) You only need this if no other imported module -depends on N.hi-boot. - - - - Orphan modules and instance declarations -- 1.7.10.4