</sect2>
+ <sect2 id="mutual-recursion">
+ <title>How to compile mutually recursive modules</title>
+
+ <indexterm><primary>module system, recursion</primary></indexterm>
+ <indexterm><primary>recursion, between modules</primary></indexterm>
+
+ <para>GHC supports the compilation of mutually recursive modules.
+ This section explains how.</para>
+
+ <para>Every cycle in the module import graph must be broken by a <filename>hs-boot</filename> file.
+ Suppose that modules <filename>A.hs</filename> and <filename>B.hs</filename> are Haskell source files,
+ thus:
+<programlisting>
+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
+</programlisting>
+<indexterm><primary><literal>hs-boot</literal>
+ files</primary></indexterm> <indexterm><primary>importing,
+ <literal>hi-boot</literal> files</primary></indexterm>
+Here <filename>A</filename> imports <filename>B</filename>, but <filename>B</filename> imports
+<filename>A</filename> with a <literal>{-# SOURCE #-}</literal> pragma, which breaks the
+circular dependency. For every module <filename>A.hs</filename> that is <literal>{-# SOURCE #-}</literal>-imported
+in this way there must exist a souce file <literal>A.hs-boot</literal>. This file contains an abbreviated
+version of <filename>A.hs</filename>, thus:
+<programlisting>
+module A where
+ newtype TA = MkTA Int
+</programlisting>
+A <filename>hs-boot</filename> file is compiled by GHC, just like a <filename>hs</filename> file:
+<programlisting>
+ ghc -c A.hs-boot
+</programlisting>
+Just as compiling <filename>A.hs</filename> produces an
+interface file <filename>A.hi</filename>, and an object file
+<filename>A.o</filename>, so compiling <filename>A.hs-boot</filename>
+produces an interface file
+<filename>A.hi-boot</filename>, and an pseudo-object file
+<filename>A.o-boot</filename>. The interface file
+<filename>A.hi-boot</filename> 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 <filename>A.hi-boot</filename> was
+last brought up to date.
+</para>
+<para>To compile these three files, issue the following commands:
+<programlisting>
+ 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
+</programlisting>
+</para>
+<para>There are several points to note here:
+<itemizedlist>
+<listitem>
+ <para>The file <filename>A.hs-boot</filename> is a programmer-written source file.
+ It must live in the same directory as its parent source file <filename>A.hs</filename>.
+ (Currently, if you use a literate source file <filename>A.lhs</filename> you must
+ also use a literate boot file, <filename>A.lhs-boot</filename>.)
+ </para></listitem>
+
+ <listitem><para> The <filename>hi-boot</filename> generated by compiling a <filename>hs-boot</filename>
+ file is in machine-generated binary format.
+ You can display its contents with <command>ghc --show-iface</commaond>. If you
+ specify a directory for interface files, the <option>-ohidir</option> flag, then that affects
+ <filename>hi-boot</filename> files too.</para></listitem>b
+
+ <listitem><para> 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.</para></listitem>
+
+ <listitem><para> When a hs-boot file <filename>A.hs-boot</filename>
+ is compiled, it is checked for scope and type errors.
+ When its parent module <filename>A.hs</filename> is compiled, the two are compared, and
+ an error is reported if the two are inconsistent.
+ </para></listitem>
+
+ <listitem><para> If hs-boot files are considered distinct from their parent source
+ files, and if a <literal>{-# SOURCE #-}</literal> import is considered to refer to the
+ hs-boot file, then the module import graph must have no cycles. The <command>ghc -M</command>
+ will report an error if a cycle is found.
+ </para></listitem>
+</itemizedlist>
+</para>
+<para>
+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 <emphasis>everything</emphasis> that module
+ <literal>A</literal> exports, only the things required by the
+ module that imports <literal>A</literal> recursively.</para>
+<para>A hs-boot file is written in a subset of Haskell:
+<itemizedlist>
+<listitem><para> The module header, and import statements, are exactly as in Haskell.</para></listitem>
+<listitem><para> There must be no value declarations, but there can be type signatures for
+values. For example:
+<programlisting>
+ double :: Int -> Int
+</programlisting>
+</para></listitem>
+<listitem><para> Fixity declarations are exactly as in Haskell.</para></listitem>
+<listitem><para> Type synonym declarations are exactly as in Haskell.</para></listitem>
+<listitem><para> 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:
+<programlisting>
+ data T a b
+</programlisting>
+ In a <emphasis>source</emphasis> program
+ this would declare TA to have no constructors (a GHC extension: see <xref linkend="nullary-types"/>),
+ 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 <emphasis>can</emphasis> also write out the constructors but, if you do so, you must write
+ it out precisely as in its real definition.</para>
+ <para>
+ If you do not write out the constructors, you may need to give a kind
+ annotation (<xref linkend="sec-kinding"/>), 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:
+<programlisting>
+ data R (x :: * -> *) y
+</programlisting>
+</para></listitem>
+<listitem><para> 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.
+</para></listitem>
+<listitem><para> 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.
+ </para></listitem>
+</itemizedlist>
+</para>
+ </sect2>
+
+
<sect2 id="using-make">
<title>Using <command>make</command></title>
.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
on the source. The rule says a <filename>.hi</filename> file
can be made from a <filename>.o</filename> file by
doing…nothing. Which is true.</para>
+ <para> Note that the suffix rules are all repeated twice, once
+ for normal Haskell source files, and once for <filename>hs-boot</filename>
+ files (see <xref linkend="mutual-recursion"/>).
<para>Note the inter-module dependencies at the end of the
Makefile, which take the form</para>
<literal>Baz.hi</literal>, then the out-of-date file must be
brought up to date. To bring it up to date,
<literal>make</literal> looks for a rule to do so; one of the
- preceding suffix rules does the job nicely.</para>
+ preceding suffix rules does the job nicely. These dependencies
+ can be generated automatically by <command>ghc</command>; see
+ <xref linkend="sec-makefile-dependencies"/></para>
- <sect3 id="sec-makefile-dependencies">
+ </sect2>
+ <sect2 id="sec-makefile-dependencies">
<title>Dependency generation</title>
<indexterm><primary>dependencies in Makefiles</primary></indexterm>
<indexterm><primary>Makefile dependencies</primary></indexterm>
<para>Now, before you start compiling, and any time you change
the <literal>imports</literal> in your program, do
<command>make depend</command> before you do <command>make
- cool_pgm</command>. <command>ghc -M</command> will
+ cool_pgm</command>. The command <command>ghc -M</command> will
append the needed dependencies to your
<filename>Makefile</filename>.</para>
- <para>In general, if module <literal>A</literal> contains the
- line
-
+ <para>In general, <command>ghc -M Foo</command> does the following.
+ For each module <literal>M</literal> in the set
+ <literal>Foo</literal> plus all its imports (transitively),
+ it adds to the Makefile:
+ <itemizedlist>
+ <listitem><para>A line recording the dependence of the object file on the source file.
<programlisting>
-import B ...blah...
+M.o : M.hs
</programlisting>
-
- then <command>ghc -M</command> will generate a dependency line
- of the form:
-
+(or <literal>M.lhs</literal> if that is the filename you used).
+ </para></listitem>
+ <listitem><para> For each import declaration <literal>import X</literal> in <literal>M</literal>,
+ a line recording the dependence of <literal>M</literal> on <literal>X</literal>:
<programlisting>
-A.o : B.hi
-</programlisting>
-
- If module <literal>A</literal> contains the line
-
-<programlisting>
-import {-# SOURCE #-} B ...blah...
-</programlisting>
-
- then <command>ghc -M</command> will generate a dependency
- line of the form:
-
+M.o : X.hi
+</programlisting></para></listitem>
+ <listitem><para> For each import declaration <literal>import {-# SOURCE #-} X</literal> in <literal>M</literal>,
+ a line recording the dependence of <literal>M</literal> on <literal>X</literal>:
<programlisting>
-A.o : B.hi-boot
+M.o : X.hi-boot
</programlisting>
-
(See <xref linkend="mutual-recursion"/> for details of
- <literal>hi-boot</literal> style interface files.) If
- <literal>A</literal> imports multiple modules, then there will
- be multiple lines with <filename>A.o</filename> as the
+ <literal>hi-boot</literal> style interface files.)
+ </para></listitem>
+ </itemizedlist>
+ If <literal>M</literal> imports multiple modules, then there will
+ be multiple lines with <filename>M.o</filename> as the
target.</para>
+ <para>There is no need to list all of the source files as arguments to the <command>ghc -M</command> command;
+ <command>ghc</command> traces the dependencies, just like <command>ghc --make</command>
+ (a new feature in GHC 6.4).</para>
<para>By default, <command>ghc -M</command> generates all the
dependencies, and then concatenates them onto the end of
</varlistentry>
</variablelist>
- </sect3>
</sect2>
- <sect2 id="mutual-recursion">
- <title>How to compile mutually recursive modules</title>
-
- <indexterm><primary>module system, recursion</primary></indexterm>
- <indexterm><primary>recursion, between modules</primary></indexterm>
-
- <para>Currently, the compiler does not have proper support for
- dealing with mutually recursive modules:</para>
-
-<programlisting>
-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
-</programlisting>
-
- <para>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
- <literal>hi-boot</literal> files, and are placed in a file
- called <filename><module>.hi-boot</filename>. To import
- from an <literal>hi-boot</literal> file instead of the standard
- <filename>.hi</filename> file, use the following syntax in the
- importing module: <indexterm><primary><literal>hi-boot</literal>
- files</primary></indexterm> <indexterm><primary>importing,
- <literal>hi-boot</literal> files</primary></indexterm></para>
-
-<programlisting>
-import {-# SOURCE #-} A
-</programlisting>
-
- <para>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 <emphasis>everything</emphasis> that module
- <literal>A</literal> exports, only the things required by the
- module that imports <literal>A</literal> recursively.</para>
-
- <para>For the example at hand, the boot interface file for A
- would look like the following:</para>
-
-<programlisting>
-module A where
-newtype TA = MkTA GHC.Base.Int
-</programlisting>
-
- <para>Notice that we only put the declaration for the newtype
- <literal>TA</literal> in the <literal>hi-boot</literal> file,
- not the signature for <function>f</function>, since
- <function>f</function> isn't used by <literal>B</literal>.</para>
-
- <para>The syntax is similar to a normal Haskell source file, but
- with some important differences:</para>
-
- <itemizedlist>
- <listitem>
- <para>Local entities (ones defined in the same <literal>hi-boot</literal> file may
- be mentioned unqualified, but non-local entities (ones defined in other modules)
- must be qualified with their
- <emphasis>original</emphasis> defining module. Qualifying
- by a module which just re-exports the entity won't do. In
- particular, most <literal>Prelude</literal> entities aren't
- actually defined in the <literal>Prelude</literal> (see for
- example <literal>GHC.Base.Int</literal> in the above
- example). HINT: to find out the fully-qualified name for
- entities in the <literal>Prelude</literal> (or anywhere for
- that matter), try using GHCi's
- <literal>:info</literal> command, eg.</para>
-<programlisting>Prelude> :m -Prelude
-> :i IO.IO
--- GHC.IOBase.IO is a type constructor
-newtype GHC.IOBase.IO a
-...</programlisting>
- </listitem>
- <listitem>
- <para>Only <literal>data</literal>, <literal>type</literal>,
- <literal>newtype</literal>, <literal>class</literal>, and
- type signature declarations may be included. You cannot declare
- <literal>instances</literal> or derive them automatically with
- a <literal>deriving</literal> clause.</para>
-</listitem>
-
-<listitem> <para>For <literal>data</literal> or <literal>newtype</literal> declaration, you may omit all
-the constructors, by omitting the '=' and everything that follows it:
-<programlisting>
-module A where
- data TA
-</programlisting>
- In a <emphasis>source</emphasis> program
- this would declare TA to have no constructors (a GHC extension: see <xref linkend="nullary-types"/>),
- 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.</para>
- <para>
- You <emphasis>can</emphasis> 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 <literal>{-# UNPACK #-}</literal> 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 <emphasis>outcome</emphasis> of this decision.
- (So in the cases where GHC decided not to unpack, you must not use the pragma.)
- Tread with care.</para>
- <para>
- Regardless of whether you write the constructors, you must write all the type parameters,
- <emphasis>including their kinds</emphasis>
- if they are not '*'. (You can give explicit kinds in source files too (<xref linkend="sec-kinding"/>),
- but you <emphasis>must</emphasis> do so in hi-boot files.)</para>
- </listitem>
-
-<listitem><para>
- In a <literal>class</literal> declararation, you may not specify any class operations; that is,
- there can be no <literal>where</literal> part. If you want to use the class operations in a recursive
- way, declare them in the <literal>hi-boot</literal> file with separate, overloaded type signatures, thus:
-<programlisting>
- class Num a
- (+) :: Num a => a -> a -> a
-</programlisting>
- </para>
- </listitem>
- </itemizedlist>
-
-<para>If <literal>M.hi-boot</literal> mentions an entity <literal>N.f</literal>, defined in some other
-module <literal>N</literal>, then GHC will by default go hunting for <literal>N.hi</literal>. If module
-<literal>N</literal> is not yet compiled either, GHC won't look for <literal>N.hi-boot</literal>; it'll just
-complain. To fix this, in the source file that uses
-<literal>import {-# SOURCE #-} M</literal>, add
-<literal>import {-# SOURCE #-} N()</literal>. (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 <literal>N.hi-boot</literal>.</para>
-
- </sect2>
-
-
<sect2 id="orphan-modules">
<title>Orphan modules and instance declarations</title>