Simple Input and Output

Handling I/O in ATS properly requires the availability of both dependent types and linear types, which I will cover elsewhere. In this section, I only present a means for allowing the programmer to access certain very basic I/O functionalities.

A file handle essentially associates a stream (of bytes) with a file identifier (represented as an integer). In ATS, the type for file handles is FILEref. There are three standard file handles, which are listed as follows:

Various functions on file handles are declared in the file prelude/SATS/filebas.sats, which is automatically loaded by atsopt. For instance, the functions for opening and closing file handles have the following interfaces:

fun open_file_exn
  (path: string, mode: file_mode): FILEref
// end of [open_file_exn]

fun close_file_exn (fil: FILEref): void

Note that these two functions abort immediately whenever an error occurs. The type file_mode is for values representing file modes, which are listed as follows:

As an example, the following short program opens a file handle, outputs the string "Hello, world!" plus a newline into the stream associated with the file handle and then closes the file handle:

//
// The following line is needed for the compiler
// to gain access to some library I/O functions:
//
staload _(*anon*) = "libc/SATS/stdio.sats"

implement
main () = () where {
  val out = open_file_exn ("hello.txt", file_mode_w)
  val () = fprint_string (out, "Hello, world!\n")
  val () = close_file_exn (out)
} // end of [main]

After executing the program, we obtain a file of the name "hello.txt" in the current working directory containing the expected content. There are various fprint-functions in ATS for printing out data into the stream in a given file handle. Often the programmer can simply use the name fprint to refer to these functions due to the support for overloading in ATS.

Another two common I/O functions are given the following interfaces:

fun input_line (fil: FILEref): Stropt
fun output_line (fil: FILEref, line: string): void

The function input_line reads a line from the stream in a given file handle, and it returns a value of the type Stropt. For the moment, let us equate Stropt with the type option0(string). If the return value is constructed by option0_none, then the stream has reached the end when input_line is called. Otherwise, the return value is of the form option0_some(str), where str represents the line read from the stream minus the ending newline symbol. The function output_line writes its second argument, which is a string, and a newline symbol into the stream associated with its first argument, which is a file handle. As an example, the following short program echos each line received from the standard input onto the standard output:

staload _(*anon*) = "libc/SATS/stdio.sats"

implement
main () = loop () where {
  fun loop (): void = let
    val line = input_line (stdin_ref)
  in
    if stropt_is_some (line) then let
      val () = output_line (stdout_ref, stropt_unsome (line))
    in
      loop ()
    end else
      () // loop exits as the end-of-file is reached
    // end of [if]
  end (* end of [loop] *)
} // end of [main]

The function stropt_is_some essentially checks whether a given value is constructed by option0_some (if we equate Strotp with option0(string)) and the function stropt_unsome extracts out the argument of option0_some in a value constructed by option0_some. Often, typing the CTRL-D character can terminate the above program for echoing each line of input.