Generic modules
A generic module like Set(T : MapKey) is specialized for each element type it is used with. Set(OrderId) and Set(address) are distinct types with their own code.
(* Generic modules specialize at compile time: no runtime cost, no casts.
`Set(T : mapKey)` accepts any type that can key a map. *)
import "std/evm";
import "./lib/set";
import "./lib/set_enumerable";
type OrderId is bytes32;
storage is record {
admins : Set(address);
orders : Set(OrderId);
members : SetEnumerable(address);
}
(* Functions can be generic too. *)
pure function first(T)(const a : T; const b : T) : T is
a
external function addAdmin(const who : address) is {
Set.add(storage.admins, who);
}
external view function isAdmin(const who : address) : bool is
Set.contains(storage.admins, who)
(* `Set(OrderId)` is not `Set(bytes32)`: the element type is checked. *)
external function track(const id : bytes32) is {
Set.add(storage.orders, OrderId(id));
}
external function join() is {
SetEnumerable.add(storage.members, first(Evm.sender(), Evm.txOrigin()));
}
external view function memberCount() : uint is
SetEnumerable.length(storage.members)
external view function memberAt(const i : uint) : address
raises OutOfBounds
is
SetEnumerable.at(storage.members, i)
(* A module passes its own storage to a generic module the same way. *)
module Watchlist is {
storage watchlist at "lithe.watchlist" is record {
watched : SetEnumerable(address);
}
external function watch(const who : address) is {
SetEnumerable.add(watchlist.watched, who);
}
external view function watchedCount() : uint is
SetEnumerable.length(watchlist.watched)
}
use Watchlist exposing {
function watch(who : address);
view function watchedCount() : uint;
}(* Membership set. One mapping, no enumeration. *)
module Set(T : MapKey) is {
type Self is map(T, bool);
view function contains(storage set : Self; const value : T) : bool is
set[value]
function add(storage set : Self; const value : T) is {
set[value] := true;
}
function remove(storage set : Self; const value : T) is {
set[value] := false;
}
}(* Membership plus enumeration. The list and the index map are both stored. *)
import "std/list";
module SetEnumerable(T : MapKey) is {
type Self is record {
values : list(T);
indexes : map(T, uint);
}
type Iterator is record {
index : uint;
}
error OutOfBounds;
view function contains(storage items : Self; const value : T) : bool is
items.indexes[value] > 0
view function length(storage items : Self) : uint is
items.values.length
view function at(storage items : Self; const index : uint) : T
raises OutOfBounds
is {
assert(index < items.values.length, OutOfBounds);
} with items.values[index]
function add(storage items : Self; const value : T) is {
if items.indexes[value] = 0 then {
List.push(items.values, value);
items.indexes[value] := items.values.length;
}
}
function remove(storage items : Self; const value : T) is {
const found : uint := items.indexes[value];
if found = 0 then {
skip
} else {
const moved : T := items.values[items.values.length - 1];
items.values[found - 1] := moved;
items.indexes[moved] := found;
items.indexes[value] := 0;
List.pop(items.values);
}
}
}Set.add(storage.admins, who)works on the storage location it is given. The set module owns no storage of its own.Watchlisthandswatchlist.watched, from its own storage, toSetEnumerablejust as the contract hands overstorage.members.MapKeylimitsTto types that can key a map.Set(string)is rejected.

