Imperative Notation

Pascal Serrarens pascalrs@cs.kun.nl
Wed, 10 Feb 1999 10:47:48 +0100


Nick Kallen wrote:

> I'm going to sketch some really informal stuff that will hopefully spur some
> more concrete discussion and debate. (I'm going to completely ignore the
> structure of the IO libraries and just try to do this as simply and as
> clearly as possible.)

Ok, I read your examples and tried to see how far we can get using the things
Clean has to offer.

Of course, I cannot provide do-expression, because that is syntax, and I cannot
create new syntax.

First let's introduce the functions I am going to use in these examples. They
are all experimental and mostly derived from functions in the StdEnv.

     readline :: !*(*ch a) !*env -> (!{#Char}, !*(*ch a), !*env)
                            | Receive ch & ThreadEnv env & toChar a

Reads a line from a stream (like a file, TCP channel, etc.)

     writes :: !{#Char} !*(*ch a) !*env -> (!*(*ch a), !*env)
                            | Send ch & ThreadEnv env & fromChar a

Writes a string on a stream.

     openFile :: !String !Int !*env -> (!*FILE, !*env) | ThreadEnv env

Opens a file.

     class CloseChannel ch
     where
          close :: !*(*ch .a) !*env -> *env | ThreadEnv env

Closes any stream (including files)


I implemented some combinators which hide away the states completely. Note that
the readline/writes functions need both a stream (state) and an environment
(state). I wanted to hide both of them, to enable file access which is as
simple as possible.

First I tried to write some combinators which hide away the states completely:

     :: ST  ref a *env :== ref -> *(env -> *(a, ref, env))
     :: ST` ref   *env :== ref -> *(env -> *(   ref, env))

     (:\\) infixr 0 :: (ST` .ref *env) (ST .ref b *env) -> ST .ref b *env
     (=\\) infixr 0 :: (ST .ref a *env) (a -> ST .ref b *env) -> ST .ref b *env
     ret            :: .a .ref *env -> (.a, .ref, *env)

(If you are interested in the code, you can ask me. I left them out for brievety)

Furthermore I implemented a function which starts a file sequence (monad).

     withFile :: {#Char} Int (ST FILE a *env) -> ET a *env | ThreadEnv env

(ET is a environment transformer, like the IO monad, see below)

Now I could write a function like:

     file_func1
        = withFile "T.icl" FReadText (
           writes "hello world" :\\
           readline             =\\ \cs1 ->
           readline             =\\ \cs2 ->
           ret (cs1, cs2)
           )

This is very nice and clean, but might be inconvenient when dealing with
more than one file simultanously.

For this, I wrote environment combinators:

     :: *ET a *env :== env -> *(a, env)
     :: *ET`  *env :== env -> env

     (:\) infixr 0 :: (ET` *env) (ET .a *env) -> ET .a *env
     (=\) infixr 0 :: (ET .a *env) *(.a -> ET .b *env) -> ET .b *env
     ret` :: .a *env -> (.a, *env)

Furthermore I needed a function which transforms a state transformer into
a environment transformer:

      et :: (ST .ref .a *env) -> (.ref -> ET (.ref, .a) *env)

Now I can write a monadic function which manipulates two files:

     file_func2 =
        openFile "T.icl" FReadText		=\ \f1 ->
        openFile "T2.icl" FWriteText	=\ \f2 ->
        et readline f1					=\ \(f1, cs) ->
        writes cs f2					=\ \f2 ->
        close f1						:\
        close f2 						:\
        ret` Void

As for the parallel composition. I implemented a parallel combinator, which
really evaluates two environment transformers concurrently (in Concurrent
Clean, of course):

     (<|>) infix 0 :: (ET .a *TState) (ET .b *TState) -> (ET (.a, .b) *env)
                      | ThreadEnv env

(TState is the environment for threads, like World is for programs)

And I can put the two file functions in parallel:

file :: ET ( (String, String), Void ) *env | ThreadEnv env
files = file_func1 <|> file_func2
 

As you see there is no need to open the file system; this is hidden.

Please tell me what you think of this. Is it (apart from do-expression
like syntax) fantastic/wonderful/good/acceptable/improvable/bad/very
very very bad???

Pascal Serrarens.