Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/tests/nofib/real/mkhprog/Main.lhs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


% Mkhprog.lhs - Haskell equivalent of mkprog

% @(#)Mkhprog.lhs	2.4 dated 92/06/23 at 15:37:01

% Crown Copyright 1992

\documentstyle[a4wide]{article}

\title{Mkhprog --- a Command line Parser Generator}
\author{N D North\\
	National Physical Laboratory\\
	Teddington, TW11 0LW, UK.\\
	{\tt ndn\@seg.npl.co.uk}}

% Some macros lifted from elsewhere to make this more standalone.
\makeatletter
% INLINE PROGRAM CODE
%
% \prog{foo} sets its argument in typewriter font.
\def\prog#1{\ifmmode\mbox{\tt #1}\else{\tt #1}\fi}

% NEWVERBATIM (from iso.sty)
%
% \newverbatim{foo} creates a new environment, foo, which behaves exactly
% like the verbatim environment except that it is delimited by
% \begin{foo} ... \end{foo}.
% See the VERBATIM section of latex.tex for the inspiration behind this.
%
\def\newverbatim#1{\expandafter\def\csname #1\endcsname{%
\\@verbatim \frenchspacing\\@vobeyspaces \csname \@x#1verbatim\endcsname}
\expandafter\let\csname end#1\endcsname=\endtrivlist
\new\@xverbatim{#1}}

\begingroup \catcode `|=0 \catcode `[= 1
\catcode`]=2 \catcode `\{=12 \catcode `\}=12
\catcode`\\=12
|gdef|new\@xverbatim#1[
|expandafter|def|csname \@x#1verbatim|endcsname##1\end{#1}[##1|end[#1]]]
|endgroup
\makeatother

\newverbatim{haskell}

\begin{document}
\maketitle

\section*{Introduction}

This document is the source code for, and description of, \prog{mkhprog} ---
a Haskell program which generates the skeleton of other Haskell programs.
In particular, it generates the code needed to parse values passed to a
program from the command line.

Given the command line flags and associated values to be interpreted by
a program, \prog{mkhprog} produces a module containing a \prog{main}
function which acquires the command line, parses any flags and values
present, and passes the values to the main body of the Haskell program
for further processing.

\prog{mkhprog} can produce standard or ``literate'' Haskell, it can
generate a module with any desired name, and it can send its output
to the standard output or to a named file.

The body of this document describes how to use \prog{mkhprog} and then
gives the code which implements it.
Annexes describe auxiliary functions, the continuation style of programming
used, and give hints and tips on compilation and general matters.

\section{\prog{mkhprog} in use}

\subsection{Installation}
\prog{mkhprog} is distributed as a literate Haskell file, \prog{Mkhprog.lhs},
and a UNIX manual page, \prog{mkhprog.1}.
\prog{Mkhprog.lhs} is also a \LaTeX\ source file which documents the program
and its usage.

To install \prog{mkhprog}, compile \prog{Mkhprog.lhs} (converting it to
standard Haskell first if necessary, as described in Annex~\ref{convert})
and place the binary somewhere accessible.
You may also want to put the manual page in your local manual directory.

\subsection{Flags}
We will first describe the command line parameters interpreted by
\prog{mkhprog} itself, and then show how it treats the remainder of its
command line.
The program interprets the following flags:
\begin{description}
\item[\prog{-l}] produces a ``literate'' Haskell script in which all lines of
    code begin with the characters \prog{"> "}.
    This file is an example of a literate Haskell script.
\item[\prog{-m} {\em module\_name}] writes a module called {\em module\_name}
    rather than the default name of \prog{Main}.
\item[\prog{-o} {\em file\_name}] writes output to {\em file\_name} rather than
    the default of standard output.
\end{description}
The first command line flag which is not a member of the above set marks
the beginning of the flags to be interpreted by the generated program, which
we will now describe.

Programs generated by \prog{mkhprog} can interpret command line flags and
associated values of any Haskell type of class \prog{Text}.
To generate a program which interprets a flag \prog{-f} and a value of
type \prog{Foo}, we invoke \prog{mkhprog} as
\begin{verbatim}
mkhprog -f Foo
\end{verbatim}
We may then invoke the generated program as
\begin{verbatim}
prog -f "MkFoo 3 True"
\end{verbatim}
if \prog{MkFoo 3 True} is a valid item of type \prog{Foo} and \prog{Foo}
is an instance of the class \prog{Text}.

If the type associated with the flag is either \prog{Bool} or \prog{String}
then the generated program behaves in a special manner as follows:
\begin{description}
\item[\prog{Bool}] the program interprets the flag without any following
value, i.e. a program generated by
\begin{verbatim}
mkhprog -b Bool 
\end{verbatim}
is invoked simply as
\begin{verbatim}
prog -b 
\end{verbatim}
with no \prog{True} or \prog{False}.

You can produce programs which interpret a Boolean flag as a toggle
by invoking \prog{mkhprog} as:
\begin{verbatim}
mkhprog +b Bool 
\end{verbatim}
The generated program may then be called with either \prog{-b} or
\prog{+b} to set an internal value to \prog{False} or \prog{True}
respectively.

Boolean flags default to \prog{False}.

\item[\prog{String}] the program interprets the flag followed by an unquoted
string, i.e.
\begin{verbatim}
mkhprog -s String 
\end{verbatim}
generates a program which is invoked as
\begin{verbatim}
prog -s string 
\end{verbatim}
rather than
\begin{verbatim}
prog -s \"string\" 
\end{verbatim}
String flags default to \prog{""} (the empty string).
\end{description}

\subsection{Output}
This section shows the output from \prog{mkhprog} and the editing needed
to produce a compilable program from it.
If \prog{mkhprog} is invoked as
\begin{verbatim}
mkhprog -b Bool -s String -f Foo
\end{verbatim}
then the following output is produced:
\begin{verbatim}
module Main (main) where

import System (getArgs)

defaultArgs :: Args
defaultArgs  =  MkArgs False "" ??

usage :: IO ()
usage  =  print "Usage: prog [-b] [-s String] [-f Foo]"

data Args  =  MkArgs Bool String Foo deriving ()

parse_args :: Args -> String -> IO ()
parse_args (MkArgs x1 x2 x3) ('-':'b':rest)
    =  readbool (parse_args (MkArgs True x2 x3)) rest
parse_args (MkArgs x1 x2 x3) ('-':'s':rest)
    =  readstring (\str -> parse_args (MkArgs x1 str x3)) rest
parse_args (MkArgs x1 x2 x3) ('-':'f':rest)
    =  readval reads (\val -> parse_args (MkArgs x1 x2 val)) rest
parse_args (MkArgs x1 x2 x3) ('-': _ :rest)
    =  usage
parse_args (MkArgs x1 x2 x3)  rest  =  prog x1 x2 x3 (lines rest)

main :: IO ()
main  = do
   argv <- getArgs
   parse_args defaultArgs (unlines argv)

and auxiliary functions ...
\end{verbatim}
The first line simply gives the module header and says that \prog{main}
is being exported.
If \prog{mkhprog} had been invoked with the \prog{-m} flag, then the
module name would have been different.

Next comes the default value of the command line arguments.
This is near the beginning as the user will probably want to edit it.
Boolean flags default to \prog{False}, strings to the empty string and
anything else to \prog{??}.
The user will need to edit \prog{??} to a sensible value before
compilation.

The function \prog{usage} is also near the beginning as it too will
probably need editing.
This function is called in case of the command line flags being invalid
and writes to the standard error a template for how to call the generated
program.

The later parts of the program should not need editing and consist of:
\begin{itemize}
\item A declaration of the \prog{Args} data type, which is used to
    carry information about command line flags.
\item The definition of \prog{parse\_args} which parses command line
    flags and associated values, gives an error message on unrecognised
    flags, and calls \prog{prog} when it hits a non-flag command line
    argument.
    \prog{prog} is then called with the values associated with the command
    line flags (or the default if the flag was not present) and any
    remaining command line arguments, split into a list of strings, one
    per argument.
\item The definition of \prog{main}, which just acquires the command line,
    puts it into a processable form,
    and passes it to \prog{parse\_args}, with \prog{defaultArgs}.
\item Auxiliary functions for reading Booleans, strings and arbitrary
    values from the command line arguments.
    These are given in Annex~\ref{auxfns}.
\end{itemize}

\subsection{Compilation}
Before compilation, the user must perform a few tasks:
\begin{itemize}
\item Edit any instances of \prog{??} in the declaration of \prog{defaultArgs}
    to sensible values.
\item Provide a definition of \prog{prog} to be called from
    \prog{parse\_args}.
\item Ensure that any user-defined data types associated with flags are in
    scope and are instances of the class \prog{Text}.
\end{itemize}
The user may also want to edit the definition of \prog{usage} to give a
more informative error message, though this is not essential.

After these changes, the program is ready for compilation.
The generated program is standard Haskell (version 1.1 or later)
and will compile on any
conforming implementation; some compilers have quirks which prevent
straightforward compilation, which are documented in Annex~\ref{quirks}.

\section{Command line parsing}

The functions which parse the command line are very similar to
those generated by \prog{mkhprog} itself.
Indeed they are edited from \prog{mkhprog} output.

The program starts with a module header which just exports \prog{main}.
\begin{haskell}

> module Main (main) where

> import System (getArgs)

\end{haskell}

The \prog{main} function acquires the command line arguments and passes
them to \prog{parse\_args}, together with their default values, encapsulated
in \prog{defaultEnv} (see Annex~\ref{env} for a description of the
environment).
\begin{haskell}

> main :: IO ()
> main  =  do
>  argv <- getArgs 
>  parse_args defaultEnv (unlines argv)

\end{haskell}

\prog{parse\_args e str} reads occurrences of \prog{-l}, \prog{-m} and
\prog{-o}, with any associated values, from \prog{str}, modifies the
environment \prog{e} appropriately and then passes the remainder of
the command line and the modified environment to \prog{next}.
\begin{haskell}

> parse_args :: Env -> String -> IO ()
> parse_args (MkEnv x1 x2 x3) ('-':'l':rest)
>     =  readbool (parse_args (MkEnv True x2 x3)) rest
> parse_args (MkEnv x1 x2 x3) ('-':'m':rest)
>     =  readstring (\str -> parse_args (MkEnv x1 str x3)) rest
> parse_args (MkEnv x1 x2 x3) ('-':'o':rest)
>     =  readstring (\str -> parse_args (MkEnv x1 x2 (File str))) rest
> parse_args e rest  =  next (pairs (lines rest)) e

\end{haskell}

\prog{pairs} splits a list of strings into a list of pairs of
strings.
If the length of its argument is odd, it adds a dummy empty string,
which will cause an error to be flagged by \prog{next}.
\begin{haskell}

> pairs :: [String] -> [(String, String)]
> pairs []         =  []
> pairs (s:s':ss)  =  (s,s') : pairs ss
> pairs [s]        =  [("",s)]

\end{haskell}

\prog{next sps} checks whether the list of pairs of strings, \prog{sps},
forms valid command line input to \prog{mkhprog}.
If not, a message is printed and the program halts; otherwise \prog{mkprog}
is called to generate the program.
\begin{haskell}

> next :: [(String, String)] -> Cont
> next sps e | check sps  =  mkprog sps e
>            | otherwise  =  usage

\end{haskell}

\prog{check sps} checks whether the list of pairs of strings, \prog{sps},
forms valid command line input to \prog{mkhprog}.
This consists of checking that
each flag is of the form \prog{-c}, or is of the form \prog{+c}
with associated type \prog{Bool}, where \prog{c} is a character
and that none of the characters are duplicated.
\begin{haskell}

> check :: [(String, String)] -> Bool
> check sps  =  flags_ok sps && no_dups [flag | (_:flag:_,_) <- sps]
>               where
>               flags_ok  =  and . map f
>               f (['-',_],_)       =  True
>               f (['+',_],"Bool")  =  True
>               f _                 =  False
>               no_dups []      =  True
>               no_dups (c:cs)  =  not (c `elem` cs) && no_dups cs

\end{haskell}

\prog{usage} prints a message saying how \prog{mkhprog} should be used.
\begin{haskell}

> usage :: IO ()
> usage = 
>     putStr
>      "Usage: mkhprog [-l] [-m module_name] [-o file_name] [[-flag type] ...]"

\end{haskell}

\prog{mkprog sps} generates and writes the program by calling a series of
functions, each generating one part.
\begin{haskell}

> mkprog :: [(String, String)] -> Cont
> mkprog sps
>     =  (do_header . nl . do_default types . nl . do_usage sps . nl
>         . do_datadecl types . nl . do_parse_args sps . nl . do_auxfns)
>        stop
>        where
>        types  =  map snd sps

\end{haskell}

\prog{do\_header} writes out the module header.
\begin{haskell}

> do_header :: Cont -> Cont
> do_header c
>     =  modname (\s -> writeln ("module " ++ s ++ " (main) where\n\nimport System (getArgs)") c)

\end{haskell}

\prog{do\_default ts} writes a declaration for the default value of the
command line argument types, \prog{ts}.
\prog{String}s default to the empty string; Booleans default to \prog{False};
all other types default to \prog{??}, which must be edited to a sensible
value by the user.
\begin{haskell}

> do_default :: [String] -> Cont -> Cont
> do_default ts
>     =  writeln "defaultArgs :: Args"
>        . writeln ("defaultArgs  =  MkArgs" ++ concat (map f ts))
>        where
>        f "String"  =  " \"\""
>        f "Bool"    =  " False"
>        f _         =  " ??"

\end{haskell}

\prog{do\_usage sps} writes a usage function, which is called if the
command line arguments are invalid.
\begin{haskell}

> do_usage :: [(String, String)] -> Cont -> Cont
> do_usage sps
>     =  writeln "usage :: IO ()"
>        . writeln ("usage  =  hPutStr stderr \"Usage: prog"
>                   ++ concat (map f sps) ++ "\"")
>        where
>        f (['-',c], "Bool")  =  " [-" ++ [c] ++ "]"
>        f (['+',c], "Bool")  =  " [(+|-)" ++ [c] ++ "]"
>        f (flag, typ)        =  " [" ++ flag ++ " " ++ typ ++ "]"

\end{haskell}

\prog{do\_datadecl ts} declares the type \prog{Args} to be made up of the
types \prog{ts}.
\begin{haskell}

> do_datadecl :: [String] -> Cont -> Cont
> do_datadecl ts
>     =  writeln ("data Args  =  MkArgs" ++ concat (map ((:) ' ') ts)
>                 ++ " deriving ()")

\end{haskell}

\prog{do\_parse\_args sps} writes the function which parses command line
arguments.
It applies \prog{do\_one\_flag} to each element of \prog{sps} and then
writes a final couple of clauses which catch invalid flags and call the
main program, \prog{prog}.
\begin{haskell}

> do_parse_args :: [(String, String)] -> Cont -> Cont
> do_parse_args sps
>     =  writeln "parse_args :: Args -> String -> IO ()"
>        . foldr (.) end
>        (zipWith3 do_one_flag (repeat n) [1..] sps)
>        where
>        n  =  length sps
>        end  =  writeln (phead n ++ wildmatch '-' ++ "usage")
>                . writeln (phead n ++ " rest  =  prog" ++ args 1 n
>                           ++ " (lines rest)")
>                

\end{haskell}

\prog{do\_one\_flag n r sp} writes one clause of the function which
parses command line arguments.
\prog{n} is the arity of \prog{MkArgs}, \prog{r} is the number of the current
argument, \prog{sp} is a pair of the flag and its type (as strings).
\begin{haskell}

> do_one_flag :: Int -> Int -> (String, String) -> Cont -> Cont
> do_one_flag n r (['+',flag], _)
>     =  writeln (phead n ++ match '-' flag ++ do_readbool n r False)
>        . writeln (phead n ++ match '+' flag ++ do_readbool n r True)
> do_one_flag n r (['-',flag], "Bool")
>     =  writeln (phead n ++ match '-' flag ++ do_readbool n r True)
> do_one_flag n r (['-',flag], "String")
>     =  writeln (phead n ++ match '-' flag ++ do_readstring n r)
> do_one_flag n r (['-',flag], _)
>     =  writeln (phead n ++ match '-' flag ++ do_readval n r)

\end{haskell}

\prog{do\_auxfns} writes out the functions used by \prog{mkhprog} which
do not change from program to program.
The functions are described in Annex~\ref{auxfns}
\begin{haskell}

> do_auxfns :: Cont -> Cont
> do_auxfns
>     =    writeln "main :: IO ()\n\
>                  \main  =  getArgs >>= (parse_args defaultArgs . unlines)"
>        . nl
>        . writeln "readbool :: (String -> IO ()) -> String -> IO ()\n\
>                  \readbool f \"\"         =  f \"\"\n\
>                  \readbool f ('\\n':cs)  =  f cs\n\
>                  \readbool f _          =  usage"
>        . nl
>        . writeln "readstring :: (String -> String -> IO ()) -> String \
>                                \-> IO ()\n\
>                  \readstring f \"\"  =  f \"\" \"\"\n\
>                  \readstring f cs@(c:cs')\n\
>                  \    =  f s t\n\
>                  \       where\n\
>                  \       st      =  if c == '\\n' then cs' else cs\n\
>                  \       (s,t1)  =  span ((/=) '\\n') st\n\
>                  \       t       =  if t1 == \"\" then t1 else (tail t1)"
>        . nl
>        . writeln "readval :: (Read a) => ReadS a \
>                             \-> (a -> String -> IO ()) -> String\n\
>                  \                       -> IO ()\n\
>                  \readval readsfn f str\n\
>                  \    =  case thing of\n\
>                  \           []    -> usage\n\
>                  \           (_:_) -> f val (if str' == \"\" \
>                                             \then str' else (tail str'))\n\
>                  \       where\n\
>                  \       thing        =  readsfn str\n\
>                  \       (val, str')  =  head thing"

\end{haskell}

\prog{phead n} returns the first part of the \prog{parse\_args} function,
where \prog{n} is the arity of \prog{MkArgs}.
\begin{haskell}

> phead :: Int -> String
> phead n  =  "parse_args (MkArgs" ++ args 1 n ++ ") "

\end{haskell}

\prog{args a b} gives a sequence of argument name strings,
\prog{xa}, $\ldots$, \prog{xb}, separated by spaces.
\begin{haskell}

> args :: Int -> Int -> String
> args a b  =  concat [" x" ++ show r | r <- [a..b]]

\end{haskell}

\prog{match mp c} returns a pattern which matches against \prog{-c}
or \prog{+c} as \prog{mp} is \prog{'-'} or \prog{'+'}.
\begin{haskell}

> match :: Char -> Char -> String
> match mp c  =  "('" ++ [mp] ++ "':'" ++ [c] ++ "':rest)\n    =  "

\end{haskell}

\prog{wildmatch mp} returns a pattern which matches against \prog{-c}
or \prog{+c}, where \prog{c} is any character at all, as \prog{mp} is
\prog{'-'} or \prog{'+'}.
\begin{haskell}

> wildmatch :: Char -> String
> wildmatch mp  =  "('" ++ [mp] ++ "': _ :rest)\n    =  "

\end{haskell}

\prog{do\_readval}, \prog{do\_readstring} and \prog{do\_readbool} produce
pieces of code for reading arbitrary values, strings and booleans
respectively.
\begin{haskell}

> do_readval, do_readstring :: Int -> Int -> String
> do_readbool   :: Int -> Int -> Bool -> String
> do_readval n r     =  "readval reads (\\val -> " ++ bits n r "val"
> do_readstring n r  =  "readstring (\\str -> " ++ bits n r "str"
> do_readbool n r b  =  "readbool (" ++ bits n r (show b)

\end{haskell}

\prog{bits n r str} produces a handy part of \prog{parse\_args}.
\begin{haskell}

> bits :: Int -> Int -> String -> String
> bits n r str
>     =  "parse_args (MkArgs" ++ args 1 (r-1) ++ " " ++ str ++ args (r+1) n
>        ++ ")) rest"

\end{haskell}

\prog{writeln str} writes the string \prog{str} to the chosen
output channel, in  literate format if appropriate,
and then executes its continuation, \prog{c}.
\prog{nl} gives a newline on the standard output.
\begin{haskell}

> writeln :: String -> Cont -> Cont
> writeln str c
>     =  islit (\b ->
>        output (\f env ->
>            let
>              str'   =  (if b then (lit str) else str) ++ "\n"
>              lit s  =  "> " ++ foldr ((++) . conv) "" s
>              conv '\n'  =  "\n> "
>              conv c     =  [c]
>	     in
>		f str' >> c env))

> nl :: Cont -> Cont
> nl c = output (\f env -> f "\n" >> c env)

\end{haskell}

\section{Miscellaneous}

\subsection{Bugs, comments and suggestions}
\prog{mkhprog} was written to implement one person's view of what a
skeleton Haskell program should look like.
It is by no means obvious that this is the best solution and any
suggestions for improvements are welcome.
Please send your bug reports, comments and suggestions to
\prog{ndn\@seg.npl.co.uk}.

\subsection{Acknowledgements}
\prog{mkhprog}'s functionality and manual page are closely based on those
of the C program \prog{mkprog}, whose author is unknown to me so I cannot
thank him or her in person.

Thanks also to the various implementers of Haskell around the world who
have borne with my continual bug reports and naive questions.
Thanks in particular to Will Partain who supplied useful patches to keep
\prog{mkhprog} in line with the changing definition of Haskell.

\appendix
\newpage
\section{Auxiliary functions}
\label{auxfns}

These are the functions produced by \prog{do\_auxfns}, which are used
by all programs written by \prog{mkhprog}.

\prog{readbool f str} checks that a Boolean flag is followed by either
newline or nothing: if it is, then \prog{f} is applied to the rest of
the string \prog{str}; if not, \prog{usage} is called.
\begin{haskell}

> readbool :: (String -> IO ()) -> String -> IO ()
> readbool f ""         =  f ""
> readbool f ('\n':cs)  =  f cs
> readbool f _          =  usage

\end{haskell}

\prog{readstring f str} reads a string, \prog{s}, from \prog{str} as follows:
\begin{itemize}
\item[] If \prog{str} begins with \verb|'\n'|, then \prog{s} is the
    sequence of characters following the newline, up to the next newline
    or the end of \prog{str}, whichever occurs first.
\item[] Otherwise \prog{s} is the initial substring of \prog{str}, up to
    the first newline or the end of \prog{str}, whichever occurs first.
\end{itemize}
\prog{f} is then applied to \prog{s} and \prog{t}, where \prog{t} is the
remainder of \prog{str} after removing \prog{s} and a newline character, if
\prog{s} is followed by one.
\begin{haskell}

> readstring :: (String -> String -> IO ()) -> String -> IO ()
> readstring f ""  =  f "" ""
> readstring f cs@(c:cs')
>       =  f s t
>          where
>          st      =  if c == '\n' then cs' else cs
>          (s,t1)  =  span ((/=) '\n') st
>          t       =  if t1 == "" then t1 else (tail t1)

\end{haskell}

\prog{readval readsfn f str} reads a value from \prog{str} in the same manner
as \prog{readstring}, but the value can be of any type of class \prog{Text}.
The parameter \prog{readsfn} is the appropriate overloading of \prog{reads}
--- this parameter is not essential but, without it, \prog{readval} is
reliant on the ``monomorphic restriction'', whose future presence in
Haskell is not guaranteed.
\prog{f} is applied to the value and the remainder of \prog{str} as in
\prog{readstring}, but if no value can be read, \prog{usage} is called.

Since \prog{mkhprog} does not itself use \prog{readval}, its code does
not form part of this literate Haskell script.
\begin{verbatim}

readval :: (Read a) => ReadS a -> (a -> String -> IO ()) -> String
		       -> IO ()
readval readsfn f str
    =  case thing of
           []    -> usage
           (_:_) -> f val (if str' == "" then str' else (tail str'))
       where
       thing        =  readsfn str
       (val, str')  =  head thing

\end{verbatim}

\section{Continuations and environments}

\subsection{Continuations}
\prog{mkhprog} is written in the continuation style, in which functions take
a continuation argument which tells the program ``what to do next''.
A continuation is a function which takes an environment parameter and gives
the rest of the program's output.
Thus we may define a data type for continuations thus:
\begin{haskell}

> type Cont  =  Env -> IO ()

\end{haskell}

The null continuation, which does nothing, is
\begin{haskell}

> stop :: Cont
> stop e  =  return ()

\end{haskell}

\subsection{Environments}
\label{env}
An environment contains all the information needed to generate the output
program, viz.
\begin{enumerate}
\item Whether it is a literate program.
\item The name of the module to be generated.
\item Whether output is to \prog{stdout} or to a file and, if the latter,
    the file's name.
\end{enumerate}
We may encompass this information in an environment data type:
\begin{haskell}

> data Env  =  MkEnv Bool String Output

\end{haskell}
Where the \prog{Bool} component is \prog{True} or \prog{False} as the
program is literate or not, the \prog{String} component is the module
name, and the \prog{Output} component gives details of the output stream
as follows:
\begin{haskell}

> data Output  =  Stdout | File String

\end{haskell}

The value of the environment is determined by the command line flags to
\prog{mkhprog}.
The default environment is
\begin{haskell}

> defaultEnv :: Env
> defaultEnv  =  MkEnv False "Main" Stdout

\end{haskell}

\subsection{Environment access}
Functions which need to access the environment use the
access functions in this section.

\prog{islit bc} applies \prog{bc} to the value of the Boolean
environment component.
\begin{haskell}

> islit :: (Bool -> Cont) -> Cont
> islit bc e@(MkEnv l _ _)  =  bc l e

\end{haskell}

\prog{modname sc} applies \prog{sc} to the string environment
component.
\begin{haskell}

> modname :: (String -> Cont) -> Cont
> modname sc e@(MkEnv _ m _)  =  sc m e

\end{haskell}

\prog{output oc} applies \prog{oc} to a function which writes its
argument to the output channel given by the \prog{Output} environment
component.
\begin{haskell}

> output :: ((String -> IO ()) -> Cont) -> Cont
> output oc e@(MkEnv _ _ Stdout)    =  oc (putStr) e
> output oc e@(MkEnv _ _ (File f))  =  oc (appendFile f) e

\end{haskell}

\section{Hints and tips}

\subsection{Compilation}
\label{quirks}
This section summarises the compilability of \prog{mkhprog} on the
compilers available at the time of writing (92/06/23).
\begin{description}
\item[Glasgow Prototype 0.41] Supports Haskell version 1.0, so
    cannot compile this file.
    Contact me if you want a version of \prog{mkhprog} that compiles under
    Haskell version 1.0.
\item[Chalmers Haskell B 0.997.5] This file (\prog{Mkhprog.lhs}) compiles and
    runs with no problems.
    Command line parameters to \prog{mkhprog} and the programs it generates
    must follow an isolated \prog{-},
    as others are interpreted by the run-time system (see the documentation).
\item[Yale 1.0-0] The Yale system does not support \prog{getArgs} so
    \prog{mkhprog} and the programs it generates are entirely useless on
    this system.
\end{description}

\subsection{Extracting a standard Haskell program}
\label{convert}
\prog{mkhprog} is distributed as a literate Haskell script.
If your compiler is unable to compile such scripts, you should type
the following UNIX command line (assuming this file is called
\prog{Mkhprog.lhs}):
\begin{verbatim}
expand Mkhprog.lhs | sed -e "/^> /\!d" -e "s/^> //" > Mkhprog.hs
\end{verbatim}
The output file \prog{Mkhprog.hs} is then standard Haskell and can be
compiled in the normal way.

\subsection{Things with literate Haskell}
Those, like myself, who like their literate Haskell surrounded by a
\LaTeX\ environment may care to use something like this:
\begin{verbatim}
mkhprog -l <flags> | perl -pe 's/^$/\n\\end{haskell}\n\\begin{haskell}\n/' > Foo.hs
\end{verbatim}
The file \prog{Foo.hs} will then have its declarations placed within a
\LaTeX\ environment called \prog{haskell}.

\end{document}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.