Types

Basic types

For each basic type gbasictype, there is an SML structure GBasicType and the corresponding SML type is GBasicType.t. The exception to this is guchar, which is treated as guint8 and does not have its own SML structure. For gint as a file descriptor, the corresponding SML structure is GFileDesc and the corresponding SML type is Posix.ProcEnv.file_desc. These SML types are not abstract but are equivalent to types from the Basis Library.

structure GChar     :> C_SCALAR_EQ_NULL where type t = char
structure GBool     :> C_SCALAR_EQ      where type t = bool
structure GShort    :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GUShort   :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GInt      :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GUInt     :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GLong     :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GULong    :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GInt8     :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GUInt8    :> C_SCALAR_EQ_NULL where type t = Word8.word
structure GInt16    :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GUInt16   :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GInt32    :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GUInt32   :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GInt64    :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GUInt64   :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GSSize    :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GSize     :> C_SCALAR_EQ_NULL where type t = LargeInt.int
structure GFloat    :> C_SCALAR         where type t = real
structure GDouble   :> C_SCALAR         where type t = real
structure GFileDesc :> C_SCALAR_EQ      where type t = Posix.ProcEnv.file_desc

When calling a function with an argument of type LargeInt.int, an exception is raised if the argument is outside the range of the C type of the corresponding parameter.

Array types

UTF-8 and file name types

For a C array that represents UTF-8 encoded text or a file name, there is an SML structure Utf8 and the corresponding SML type is Utf8.t. The SML type is not abstract but is equivalent to string from the Basis Library and is, therefore, not mutable.

signature UTF_8 =
  C_ARRAY
    where type t = string
    where type elem = char
    where type sequence = string
structure Utf8 :> UTF_8

Although an SML string can contain UTF-8 encoded text, Basis Library string operations, that assume 8 bit characters, will not generally work. To perform operations on UTF-8 encoded text that is not in the ASCII subset, an alternative library is required.

C array types

For each C array type whose element type has an SML structure Elem, there may be SML structures as follows:

C array

size

zero terminated

separate parameter

elements

packed inline

ElemCArray

ElemCArrayN

referenced by a pointer

ElemCPtrArray

ElemCPtrArrayN

These structures are collectively referred to as ElemC[Ptr]Array[N]. The corresponding SML type is ElemC[Ptr]Array[N].t. The SML type is abstract: internally it is a C array and is, therefore, mutable.

structure ElemCArray     :> C_ARRAY   where type elem = Elem.t
structure ElemCPtrArray  :> C_ARRAY   where type elem = Elem.t
structure ElemCArrayN    :> C_ARRAY_N where type elem = Elem.t
structure ElemCPtrArrayN :> C_ARRAY_N where type elem = Elem.t

The signature C_ARRAY specifies various array operations. The signature C_ARRAY_N includes C_ARRAY and specifies additional operations that allow the array to be treated as a ‘right hand slice’: the first index is always zero but the last index can be changed to exclude trailing elements in the underlying array.

Each structure ElemC[Ptr]Array[N] exists only if the SML type ElemC[Ptr]Array[N].t is referenced because the family of such structures is infinite, given arrays of arrays.

Note that if Elem is the structure for a named type Namespace.Type:

  • Elem has the form NamespaceType

  • The SML type NamespaceTypeC[Ptr]Array[N].t may be referenced from either Namespace or another namespace. In the former case only, the signature NAMESPACE specifies an equivalent local structure TypeC[Ptr]Array[N] so there is an equivalent SML type Namespace.TypeC[Ptr]Array[N].t.

Enumeration types

For each enumeration type Enum in a namespace Namespace, there is an SML structure Namespace.Enum and the SML type for the enumeration is Namespace.Enum.t which is a datatype that has a parameterless constructor for each enumeration literal. The structure Namespace.Enum implements the signature NAMESPACE_ENUM, which has the following form:

signature NAMESPACE_ENUM =
  sig
    datatype t =
      LITERAL_1
    | LITERAL_2
    | …
    …
  end

For example, the structure Gtk.ButtonsType implements the signature GTK_BUTTONS_TYPE, which is defined as follows:

signature GTK_BUTTONS_TYPE =
  sig
    datatype t =
      NONE
    | OK
    | CLOSE
    | CANCEL
    | YES_NO
    | OK_CANCEL
    …
  end

Flags (bitfield) types

For each flags (bitfield) type Flags in a namespace Namespace, there is an SML structure Namespace.Flags and the SML type for the flags is Namespace.Flags.t. For each predefined flags value VALUE, there is an SML value Namespace.Flags.VALUE. The structure Namespace.Flags implements the signature NAMESPACE_FLAGS, which includes the signature BIT_FLAGS to provide operations on the flags as follows:

signature NAMESPACE_FLAGS =
  sig
    eqtype t
    include BIT_FLAGS where type flags = t

    val VALUE_1 : t
    val VALUE_2 : t
    …
  end

For example, the structure Gtk.StateFlags implements the signature GTK_STATE_FLAGS, which is defined as follows:

signature GTK_STATE_FLAGS =
  sig
    eqtype t
    include BIT_FLAGS where type flags = t

    val NORMAL : t
    val ACTIVE : t
    val PRELIGHT : t
    val SELECTED : t
    val INSENSITIVE : t
    val INCONSISTENT : t
    val FOCUSED : t
    …
  end

Class types

For each class Class in a namespace Namespace, there is an SML structure Namespace.Class.

The SML type for Class or any subclass of Class is

'a Namespace.Class.class

where 'a is a type variable. The SML type for Class but not a subclass of Class is

base Namespace.Class.class

which is abbreviated to

Namespace.Class.t

The structure Namespace.Class implements the signature NAMESPACE_CLASS, which has the following form:

signature NAMESPACE_CLASS =
  sig
    type 'a class
    …
    type t = base class
    …
  end

At run time, an object with type Namespace.Class.t may actually be an instance of a subclass of Class even though the type does not indicate this. The SML types are static and represent what is known at compilation time.

There is also a structure Namespace.ClassClass but applications do not need to use this. Even if Namespace.ClassClass is not referenced in code, it may be referenced in type error messages from a compiler because the type 'a Namespace.ClassClass.class is equivalent to the type 'a Namespace.Class.class.

Unlike an object-oriented language, it is necessary for types to distinguish where an object of any subclass is permitted. Although type safety is guaranteed — a program that incorrectly assumes a subclass is rejected by type checking — it is useful, in practice, to understand the general rule, which is described in the section for object types.

Class encoding

In order to understand object type errors reported by SML compilers, it is necessary to understand the type encoding for classes because type error messages will be presented in terms of this encoding. The encoding is equivalent to that described in mGTK: An SML binding of Gtk+, Section 4.

The object type for a class C or any subclass of C is

'a C.class

The type parameter, indicated by the type variable 'a, may be instantiated to constrain the type to a subclass of C by instantiating it with the witness type of a child class. Each derived class D has its own witness type 'b d that creates a unique type for the class relative to its parent. If D is a child class of C, the object type for D or any subclass of D is

'b d C.class

This type is abbreviated to

'b D.class

In turn, the type parameter, indicated by the type variable 'b, may be instantiated to constrain the type to a subclass of D.

The type for a class is constrained to be none of its subclasses by using the type base for the witness type. Therefore, the type of class C but not a subclass of C is

base C.class

This type is abbreviated to

C.t

Given the class hierarchy

  • C
    • D
      • E
        • F

the following types are equivalent, and may be used interchangeably in type error messages:

  • 'a F.class

  • 'a f E.class

  • 'a f e D.class

  • 'a f e d C.class

Similarly, the following types are equivalent:

  • F.t

  • base F.class

  • base f E.class

  • base f e D.class

  • base f e d C.class

Interface types

For each interface Iface in a namespace IfaceNamespace, there is an SML structure IfaceNamespace.Iface and the SML type for the interface is IfaceNamespace.Iface.t. The structure IfaceNamespace.Iface implements the signature IFACE_NAMESPACE_IFACE, which has the following form:

signature IFACE_NAMESPACE_IFACE =
  sig
    type t
    …
  end

An interface is currently represented as a child class of GObject.Object (the root class). This would be problematic for an interface implemented by a class descended from a different root class. However there are no such interfaces at the moment. Applications should avoid treating an instance of IfaceNamespace.Iface.t as an instance of GObject.Object.t, even though the SML types allow this.

Due to this representation, there is also a structure IfaceNamespace.IfaceClass but applications do not need to use this. Even if IfaceNamespace.IfaceClass is not referenced in code, it may be referenced in type error messages from a compiler because the type 'a IfaceNamespace.IfaceClass.class is equivalent to the type 'a IfaceNamespace.Iface.class.

Implemented interfaces

The type encoding for a class hierarchy captures only the single-inheritance relationship of classes. Therefore, for a class or interface Type in a namespace Namespace that implements an interface Iface in a namespace IfaceNamespace, the SML type Namespace.Type.t is not an instance of IfaceNamespace.Iface.t and explicit conversion is required between these types. Conversion is provided by the function Namespace.Type.asIface which is internally the identity function.

For example, the function Gtk.Button.asActionable converts an instance of 'a Gtk.Button.class to Gtk.Actionable.t.

The specification of Namespace.Type.asIface in the signature NAMESPACE_TYPE has the form

  • val asIface : 'a class -> IfaceNamespace.Iface.t

    if Type is a class;

  • val asIface : t -> IfaceNamespace.Iface.t

    if Type is an interface.

(In the case that IfaceNamespace is the same as Namespace, note that a local name iface_t would be used instead of IfaceNamespace.Iface.t in the above specifications.)

Record types

For each record (i.e. struct type) Record in a namespace Namespace, there is an SML structure Namespace.Record and the SML type for the record is Namespace.Record.t. The structure Namespace.Record implements the signature NAMESPACE_RECORD, which has a form equivalent to the following:

signature NAMESPACE_RECORD =
  sig
    type t
    …
  end

Union types

For each union Union in a namespace Namespace, there is an SML structure Namespace.Union.

The SML type for any field of Union or an unknown field of Union is

'a Namespace.Union.union

where 'a is a type variable. The SML type for an unknown field of Union is

base Namespace.Union.union

which is abbreviated to

Namespace.Union.t

The structure Namespace.Union implements the signature NAMESPACE_UNION, which has a form equivalent to the following:

signature NAMESPACE_UNION =
  sig
    type 'a union
    type t = base union
    val toBase : 'a union -> base union
    …
  end

The only union type currently provided by Giraffe Library is Gdk.Event whose classification function is ClassifyEvent.classify.