Example:
list.gi:
// this is to output information,
// that we implement type `t', but not to
// disclosure what it is.
type <'a>t;
'a hd(<'a>t x);
<'a>t tl(<'a>t x);
int cnt;
<'a>t create();
<'b>t map(*('a) -> 'b f, <'a>t l);
void iter(*('a) -> void f, <'a>t l);
list.g:
// this is local datatype.
opt_struct <'a>t {
'a data;
<'a>opt_struct next;
}
// this will be exported out (`public')
'a hd(<'a>t x) { return x.data; }
<'a>t tl(<'a>t x) { return x.next; }
// this is local, can't be called from outside the module
void helper(<'a>t x) { ... }
// and more publics
int cnt;
<'a>t create() { cnt++; return null; }
<'b>t map(*('a) -> 'b f, <'a>t l) { ... }
void iter(*('a) -> void f, <'a>t l) { ... }
// ...
Then if you want to use the module, it can be done with :: notation, like this:
<int>List::t l = List::create();
...
int k = List::hd(l);
In case of some modules it might be useful to open them, i.e. import all symbols from module intro current namespace, so you no longer have to use :: notation (but you still can, it is often suggested for readability):
open List;
...
<int>List::t l = List::create();
...
int k = hd(l);
<int>List rest = tl(l);