[clean-list] Advice on style, please

alanh@dcs.kcl.ac.uk alanh@dcs.kcl.ac.uk
Sun, 16 Dec 2001 22:59:16 +0000 (GMT)


An appeal for guidance on style:
I have to introduce a class of 2nd year undergraduates to functional
programming.  This is actually just one part of an introductory course on
operational semantics, but the decree is that I should teach the 
semantics of imperative and functional and "logic" styles of programming, 
and the class have so far only seen Java.

I want to give the class samples of code in all three styles.
The chosen examples are
  the "Hello World!" program
  the echo program
  the copy program.
All should be robustly written, with adequate error messages.
Below is my first working version of the copy program in Clean.
Below that is a working version of the copy program in C.

The problem is that the C version appears much simpler and more economical.
I don't like this.
Functional programming is supposed to make programming simpler.
How can this version be improved?

I don't want to use the "#" notation, as that obscures the declarative
nature of the language.

One problem is the need to supply seemingly real files as values when
the program has already detected an error.  The type system demands it.
It would be nice if there were a "null file" which could act as a 
place holder, where the code below uses silly calls of the form
    fopen "" FReadText w1
Any suggestions?

Regards
Alan Hutchinson
Dept. of Computer Science
King's College London

--------------------------------------------------------------------------

module copy
import StdEnv
import ArgEnv

Start :: *World -> *World

Start w0  =  w6
where
  (console1, w1)    =  stdio w0
  argv              =  getCommandLine
  argc              =  size argv
  
  (console2, (fiOK, fi0, w2))  =  state2
  where
    state2
    | argc <> 3  =  (console1 <<< "Usage:\ncopy <input file> <output file>\n",
                               fopen ""       FReadText w1)
    | otherwise  =  (console1, fopen argv.[1] FReadText w1)
  
  (console3, (foOK, fo0, w3))  =  state3
  where
    state3
    | argc <> 3  =  (console2, fopen ""       FWriteText w2)
    | not fiOK   =  (console2 <<< ("Can't open file " +++ argv.[1] +++ "\n"),
                               fopen ""       FWriteText w2)
    | otherwise  =  (console2, fopen argv.[2] FWriteText w2)
  
  (console4, (ccOK, fi1, fo1))  =  state4
  where
    state4
    | not fiOK   =  (console3, (False, fi0, fo0))
    | not foOK   =  (console3 <<< ("Can't open file " +++ argv.[2] +++ "\n"),
                               (False, fi0, fo0))
    | otherwise  =  (console3, cc (fi0, fo0))
  
  console5
  | not foOK   =  console4
  | not ccOK   =  console4 <<< "failed.\n"
  | otherwise  =  console4
  
  w4  =  snd (fclose console5 w3)
  w5  =  snd (fclose fi1 w4)
  w6  =  snd (fclose fo1 w5)

cc :: (*File, *File) -> (Bool, *File, *File)
cc (fi0,fo0)
| atEnd      =  (True,  fia, fo0)
| not ok     =  (False, fib, fo0)
| otherwise  =  cc (fib, fwritec c fo0)
where
  (atEnd, fia)  =  fend fi0
  (ok, c, fib)  =  freadc fia

--------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>

main(int argc, char *argv[]) {
char c;
FILE *iFile, *oFile;

  if (argc != 3) {
    fprintf(stderr, "Usage:\ncopy <fromFile> <toFile>\n");
    exit(1);
  }
  iFile = fopen(argv[1],"r");
  if (iFile <= 0) {
    printf("Cannot open input file %s\n", argv[1]);
    exit(1);
  }
  oFile = fopen(argv[2],"w");
  if (oFile <= 0) {
    printf("Cannot open output file %s\n", argv[2]);
    exit(1);
  }
  
  c = getc(iFile);
  while (c != EOF) {
    putc(c,oFile);
    c = getc(iFile);
  }
}