Claus Reinke wrote:

[Snip nice module explanation/suggestion]

> Of course, if your MyCompiler had only one entry function, a local 
> definition instead of the local module would do:
> 
>   MyCompiler argv = 
>     let
>       parse =
>       type =
>       ...
>      in parse >> type >> ..
> 

I think this was an excellent explanation of the situation.

It shows that one can in fact solve all the problems with configurable
constants just by writing the whole program as a giant let-clause.

I would claim it has really nothing to do with first-class modules
or parameterized modules. They can be simulated by ordinary Haskell
data types with named fields.

The main problem seems instead to be the practical difficulties with
writing a big function as a single let-clause. I.E, the program would
be something like:

main =  readArgs >>= \argv ->
        compile argv

compile argv =
    let
        <parser functions>
        <optimizing functions>
        <thousands of lines of more functions>
    in
         do_compile


In Haskell, you can not split that giant compile function in several
files.  I'd guess a first approximation to the solution could be
something almost equivalent to #include in c.

compile argv =
    let
        include "Parser_functions.hs"
        include "Optimizing_functions"
        <and so on>
    in
        do_compile


To make this work, it seems to me the only special thing this include
directive has to do, is to indent the source code to the same position
it is placed on itself.

That should work in principle. However, there are likely to be
severe problems of both practical and more subtle nature.

First, the compiler may not be able to separate-compile the files
effectively without some hints. 

Second, there may be problems with error reporting.  I find myself not
wanting to write big functions, because it can be very difficult to
analyze the error messages and find out where the source of a tricky
type error is really to be found. But maybe that's just a matter of
putting in more type declarations. (Another reason to not make giant
functions I find to be the pain of too many indentation levels, but
that should be taken care of by the include directive.)

I don't see any more subtle problems right now... The main problem
seems to be the separate compilation issue.

In Ada they have structured a similar kind of code inclusion with
something called "subunits".

I have only the Ada 83 manual handy at the moment, but I don't remember
seeing any changes to this part last I looked at Ada 95. So here goes... 

[Begin Quote, BOLD FONT for reserved words]

10.2 Subunits of Compilation Units

A subunit is used for the separate compilation of the proper body of
a program unit declared within another compilation unit. This method
of splitting a program permits hierarchical program development.

body_stub ::=
         subprogram_specification IS SEPARATE
    |    PACKAGE BODY package_simple_name IS SEPARATE;
    |    TASK BODY task_simple_name IS SEPARATE;    

subunit ::=
    SEPARATE (parent_unit_name) proper_body

[End Quote]

Also using the package syntax from Ada (to avoid the risk of
overloading the Haskell Module Concept excessively) a direct
translation of the compiler example to use these subunit directives
(without semicolons ;-) would be:

-- file Main.hs:
package body Main is

main =  readArgs >>= \argv ->
        compile argv

compile argv =
    let
        package body Parser_functions is separate
        package body Optimizing_functions is separate
        <and so on>
    in
        do_compile

        

-- file Parser_functions.hs:
separate(Main)  -- This directive is required for separate compilation
                -- of this file. It establishes visibility of the identifiers
                -- that was visible at the point of "inclusion" in Main.
package body Parser_functions is
parse parser_input =
    let
        package body Scanning_functions is separate
        parseExpression = ...getToken...
    in
        ...
..

-- file Scanning_functions.hs
separate(Parser_functions)
getToken = ...argv...parser_input...

-- file Optimizing_functions.hs
separate(Main)
package body  is
optimize = ...argv...

-- <and so on>

----------

In Ada, they separate specification and implementation of packages.  I
think this is partly for information hiding purposes and partly to
help the compiler to separate-compile efficiently.  I have only used
the package body part in this example, assuming the specifications be
generated in some magic way. But I actually think that also Haskell
could take advantage of separation of specification and implementation.

So in summary, the extension that seems to be needed is some directive
that works basically as #include, except being more structured and
allowing separate compilation and information hiding.

I have no clue of how difficult it actually would be to implement this
kind of separate compilation - maybe Simon or someone else can say
something about it?

Are there subtle or distinct problems that I haven't thought of? After
all, its Saturday so you never know...

Sverker Nilsson




Reply via email to