Functions

A function is defined in either a namespace or a named type (class, interface, record, union, enum or bitfield). (Although a callback is a named type, it does not define any functions.) No function is defined at the top level, outside a namespace.

A function may be marked as a method or a constructor:

  • A function in a class or interface may be a method if its first argument is an instance of the class or interface.

  • A function in a class or interface may be a constructor if it returns a new instance.

SML has no concept of classes so this distinction is not particularly significant except that the instance argument of a method is treated differently in the SML interface, as described below.

Referring to functions

For a function someFunctionName in a namespace Namespace, the hierarchical SML name is Namespace.someFunctionName.

For example, the function shellParseArgv in the namespace GLib is GLib.shellParseArgv.

For a function someFunctionName in a named type Namespace.Type, the hierarchical SML name is Namespace.Type.someFunctionName.

For example, the function grabFocus in the class Gtk.Widget is Gtk.Widget.grabFocus.

The arguments of an introspected function are classified as ‘in’, ‘out’ or ‘inout’, indicating the direction in which the argument data flows. To enable a function to write to ‘out’ and ‘inout’ arguments with the C calling convention, such arguments are references. In the SML interface, a function does not use references for ‘out’ and ‘inout’ arguments because its arguments contain only initial values and the final values of are part of the returned value.

The specification of someFunctionName in the signature NAMESPACE or NAMESPACE_TYPE has the form:

  • val someFunctionName : inTypes -> retOutTypes

    if someFunctionName is not a method;

  • val someFunctionName : instanceType -> inTypes -> retOutTypes

    if someFunctionName is a method of a class or interface.

where

inTypes

contains the types of the ‘in’ and ‘inout’ arguments, as a tuple if there are two or more and as unit if there are none;

retOutTypes

contains the following types as a tuple if there are two or more and as unit if there are none:

  • the types of the ‘out’ and ‘inout’ arguments, if the return type is void or there is an error ‘out’ argument;

  • the return type followed by the types of the ‘out’ and ‘inout’ arguments, if:

    • the return type is not void, and

    • the return type is not gboolean or there are no ‘out’ arguments, and

    • there is no error ‘out’ argument

  • the types of the ‘inout’ arguments followed by an optional type containing the types of the ‘out’ arguments as a tuple if there are two or more, if the return type is gboolean and there are ‘out’ arguments but none is an error ‘out’ argument;

instanceType

is:

  • 'a class for a class, which allows an instance of the class or any subclass of the class;

  • t for an interface.

The section Calling functions, below, provides examples showing instantiations of these types.

Correspondence with C function names

In C libraries that support GObject Introspection, function names have a hierarchical form that corresponds to the hierarchical SML name as follows:

C identifier

SML identifier

⌜c_prefix⌝_⌜type_name⌝_⌜method_name⌝

⌜NamespaceName⌝.⌜TypeName⌝.⌜methodName⌝

⌜c_prefix⌝_⌜type_name⌝_⌜constructor_name⌝

⌜NamespaceName⌝.⌜TypeName⌝.⌜constructorName⌝

⌜c_prefix⌝_⌜type_name⌝_⌜function_name⌝

⌜NamespaceName⌝.⌜TypeName⌝.⌜functionName⌝

⌜c_prefix⌝_⌜function_name⌝

⌜NamespaceName⌝.⌜functionName⌝

The relationship between c_prefix and NamespaceName is, in principle, arbitrary but standard libraries use a standard mapping as follows:

c_prefix

NamespaceName

g, glib

GLib

g

GObject

g

Gio

g

GModule

atk

Atk

pango

Pango

gdk

Gdk, GdkX11

gtk

Gtk

gtk_source

GtkSource

The relationship between other placeholders is simply the difference in character case and separator convention as indicated by the placeholder names. For instance, type_name and TypeName represent identifiers containing the same words where

  • type_name indicates that all words are lowercase and separated by underscore characters;

  • TypeName indicates that all words have an uppercase initial character and lowercase subsequent characters and are not separated by any character.

For example, to convert a C identifier to an SML identifier, first split the name into its hierarchical components and then convert each field:

C identifier

SML identifier

g_io_add_watch

g.io_add_watch

GLib.ioAddWatch

gtk_text_buffer_insert_at_cursor

gtk.text_buffer.insert_at_cursor

Gtk.TextBuffer.insertAtCursor

Calling functions

Non-method functions

For a non-method introspected function, the corresponding SML function takes a tuple for the inputs containing the (before) value of the ‘in’ and ‘inout’ arguments and returns a tuple for the outputs containing the return value, if the return type is neither void nor gboolean, followed by the (after) values of the non-error ‘out’ and ‘inout’ arguments.

For example, given a call to a non-method C function f as follows:

ret = f (in1, &inout2, &out3, &out4, in5, &error);

the corresponding SML function could be called as follows:

val (ret, inout2', out3, out4) = f (in1, inout2, in5)

Note that the argument error is not treated like a normal ‘out’ argument in SML as described in the section Errors, below.

If the return type is void, no return value is included in the returned tuple.

For example, if the return type of f is void, the corresponding SML function could be called as follows:

val (inout2', out3, out4) = f (in1, inout2, in5)

A tuple must have two or more elements. Therefore, if the input or output tuple would have:

  • no elements, it is replaced by the unit value ();

  • one element, it is replaced by that element.

For example, if an introspected function get_x has no arguments and has a non-void return type, the corresponding SML function could be called as follows:

val x = getX ()

and if an introspected function set_x has one ‘in’ argument and has return type void, the corresponding SML function could be called as follows:

val () = setX x

Method functions

For a method function, the corresponding SML function takes the instance object as an initial curried argument and returns an SML function that takes the remaining arguments as described above for a non-method function.

For example, given a call to a method m on an object obj in C as follows:

ret = m (obj, in1, &inout2, &out3, &out4, in5, &error);

the corresponding SML function could be called as follows:

val (ret, inout2', out3, out4) = m obj (in1, inout2, in5)

There is no partial evaluation after applying the SML function to just the instance argument.

If the method function is defined in an interface but the instance argument has the type of a class that implements the interface, the instance argument must be converted explicitly as described by the section Implemented interfaces.

For example, if the method function m is from an interface IfaceNamespace.Iface and obj has the type of a class ClassNamespace.Class that implements the interface, the SML function m could be called as follows:

val (ret, inout2', out3, out4) = m (ClassNamespace.Class.asIface obj) (in1, inout2, in5)

Argument types

The SML type of an argument or the return value is based on the corresponding C type as described in types.

If an argument or return value has an introspection annotation that marks it as optional, its SML type is t option where t is the SML type corresponding to the C type of the argument.

Object types

An object value that is an instance of any subclass of C is guaranteed to be an instance of a class C. To avoid the need to explicitly upcast object values, the object type for a class C or any subclass of C is used where an instance of class C is required for

  • the argument of a constructor, method or function and

  • the return value of a callback function

Conversely, an object value that is guaranteed to be an instance of a class C is not guaranteed to be an instance of any subclass of C. Therefore the object type for a class C but not a subclass of C is used where an instance of class C is required for

  • the return value of a constructor, method or function and

  • the argument of a callback function

Unsupported arguments

An introspected function may allow ‘out’ (and, possibly, ‘inout’) arguments to be omitted when called by passing a null reference. The corresponding SML function does not omit any argument so the introspected function is called with all arguments.

Introspected functions that use varargs have no SML equivalent. Alternative functions should be available that provide the same capablity.

Conditional ‘out’ arguments

An introspected function may write to ‘out’ arguments conditionally. To avoid uninitialized values in SML, the value of an ‘out’ argument is not returned by the corresponding SML funtion when it is not written.

At present, a heuristic must be used to determine which ‘out’ arguments are written conditionally because GObject introspection annotations do not specify this information.

  • For an introspected function without an error argument that does not have return type gboolean, all ‘out’ arguments are considered to be written unconditionally.

  • For an introspected function without an error argument that has return type gboolean, all ‘out’ arguments are considered to be written conditionally, being written if and only if the function returns true.

  • For an introspected function with an error argument, it is assumed that if no error is raised, all (non-error) ‘out’ (and ‘inout’) arguments are written, as described in the section Errors, below.

(A proposal to introduce GObject Introspection annotations that specify the condition under which an ‘out’ argument is written is given in the comment https://bugzilla.gnome.org/show_bug.cgi?id=626721#c3.)

An introspected function without an error argument whose return type is gboolean returns a tuple for the outputs containing an optional tuple containing the (after) values of the ‘out’ arguments conditional on the return value, which is non-nil if and only if the return value is true, followed by the (after) values of ‘inout’ arguments.

For example, given a call to a C function h whose return type is gboolean as follows:

if (h (in1, &inout2, &out3, &out4, in5))
{
  /* can use inout2, out3, out4 */
  …
}
else
{
  /* can use only inout2 */
  …
}

the corresponding SML function could be called as follows:

let
  val (inout2', condOuts) = h (in1, inout2, in5)
in
  case condOuts of
    SOME (out3, out4) => …
  | NONE => …
end

A tuple must have two or more elements. Therefore, if the tuple for the conditional outputs would have one element, it is replaced by that element.

Errors

A C function reports an error by including an ‘out’ argument of type GError * as the last argument. (The extra level of indirection for an ‘out’ argument means that the C type will actually be GError **.)

For example, the declaration of the non-method C function f mentioned in an earlier example, which case raise an error, is as follows:

ty
f (ty1      in1,
   ty2     *inout2,
   ty3     *out3,
   ty4     *out4,
   ty5      in5,
   GError **error);

An error is raised by a C function if the (after) value of the error ‘out’ argument is a reference to a GError. Otherwise no error is raised.

For example, the C function f could be called as follows, assuming the return type ty is not void:

GError *error = NULL;

ret = f (in1, &inout2, &out3, &out4, in5, &error);

if (error != NULL)
  {
    /* handle error, cannot use ret, inout2, out3 and out4 */
    …
  }
else
  {
    /* no error, can use ret, inout2, out3 and out4 */
    …
  }

If an error is raised, the corresponding SML function raises the exception GLib.Error (ex, error) where

  • ex of type exn is the error exception, which allows the domain and code to be pattern matched

  • error of type GLib.Error.t is the (after) value of the error ‘out’ argument.

Therefore, the SML function provides neither the return value nor the (after) values of non-error ‘out’ and ‘inout’ arguments of the corresponding introspected function if an error is raised: it is assumed that these provide no information further to the error.

The SML function returns a tuple for the outputs containing the return value, if the return type is neither void nor gboolean, followed by the (after) values of the non-error ‘out’ and ‘inout’ arguments.

For example, the SML function corresponding to C function f could be called as follows:

let
  val (ret, inout2', out3, out4) = f (in1, inout2, in5)
in
  …
end
  handle
    GLib.Error (_, error) => …

As the tuple of outputs is returned by the SML function only if no exception is raised, it does not include the (after) value of the error ‘out’ argument because it would never contain an error. Also, the return value in the output tuple never has an error-specific value so it is not included in the output tuple if the return type is gboolean because it is assumed that a boolean return value indicates success, i.e. no error, so would always return true. The non-error ‘out’ arguments are assumed to be written unconditionally so the section Conditional ‘out’ arguments does not apply if there is an error ‘out’ argument.

For example, the C function f could be called as follows if the return type ty is gboolean (i.e. the return value indicates success):

GError *error = NULL;

if (!f (in1, &inout2, &out3, &out4, in5, &error))
  {
    /* handle error, cannot use inout2, out3 and out4 */
    …
  }
else
  {
    /* no error, can use inout2, out3 and out4 */
    …
  }

and the corresponding SML function could be called as follows:

let
  val (inout2', out3, out4) = f (in1, inout2, in5)
in
  …
end
  handle
    GLib.Error (_, error) => …