Main Program

The following portability considerations affect the form of the main program:

  • To build an executable with Poly/ML, a single function of type unit -> unit is required for the main program.

  • In the event of an unhandled exception at the top-level, an executable built with some compilers may not report it nor exit with a non-zero status, so it is recommended that the main program explicitly does this to aid diagnostics.

With this in mind, a portable main program may have the following form:

fun main () =
  let
    …
  in
    Giraffe.exit status
  end
    handle e => Giraffe.error 1 ["Uncaught exception\n", exnMessage e, "\n"]

For convenience, functions from the special structure Giraffe are used to exit the application.

To build an executable from the main program, see the sections on building with MLton and Poly/ML.

The main program typically performs some initial set up and then runs an event loop. There is more than one way to do this for a GTK application, as shown in the following sections.

A non-GTK program may use GLib without running an event loop. Such a program simply needs to initialize the GObject dynamic type system using GObject.typeInit if the version of GLib is older than 2.36. This function is deprecated and has no effect since GLib 2.36.

fun main () =
  let
    val () = GObject.typeInit ()  (* in case GLib < 2.36 *)
    …
  in
    Giraffe.exit status
  end
    handle e => Giraffe.error 1 ["Uncaught exception\n", exnMessage e, "\n"]

Using the class Gtk.Application

For a GTK application, the class Gtk.Application, based on Gio.Application, provides many useful features. Using this class, a main program could have the following form:

fun main () =
  let
    val app = Gtk.Application.new (SOME appId, Gio.ApplicationFlags.flags […])
    …

    val argv = Utf8CPtrArrayN.fromList (CommandLine.name () :: CommandLine.arguments ())
    val status = Gio.Application.run app argv
  in
    Giraffe.exit status
  end
    handle e => Giraffe.error 1 ["Uncaught exception\n", exnMessage e, "\n"]

It is common to set up GTK objects in the handler for the “activate” signal as follows:

fun activate app () =
  let
    …  (* set up *)
  in
    ()
  end

fun main () =
  let
    val app = Gtk.Application.new (SOME appId, Gio.ApplicationFlags.flags [])
    val id = Signal.connect app (Gio.Application.activateSig, activate app)

    val argv = Utf8CPtrArrayN.fromList (CommandLine.name () :: CommandLine.arguments ())
    val status = Gio.Application.run app argv

    val () = Signal.handlerDisconnect app id
  in
    Giraffe.exit status
  end
    handle e => Giraffe.error 1 ["Uncaught exception\n", exnMessage e, "\n"]

Using Gtk.init and Gtk.main

The class Gtk.Application was not available in GTK 2 and applications were required to use Gtk.init and Gtk.main to initialize GTK and run an event loop. This approach is still possible with GTK 3. Using this approach, a main program could have the following form:

fun main () =
  let
    val argv = Utf8CPtrArrayN.fromList (CommandLine.name () :: CommandLine.arguments ())
    val … = Gtk.init argv

    …  (* set up *)

    val () = Gtk.main ()
  in
    Giraffe.exit 0
  end
    handle e => Giraffe.error 1 ["Uncaught exception\n", exnMessage e, "\n"]

Gtk.init initializes GTK and parses command line options. The function should be passed the command line (program name and arguments) and returns any arguments not consumed by GTK.

Gtk.main enters the GTK main loop. The main loop runs until Gtk.mainQuit is called (from a signal handler) whereupon Gtk.main returns.