I will give the algorithmic complexity of each of my function
implementations, and briefly discuss how I derived them.

emptySet:
O(1). The function always yields an empty set and takes no arguments.

addElem:
The same complexity as the List.mem function, which I assume to be O(n)
(n is the number of elements in the set). The function first has to check
if the element is already in the list, for which it uses List.mem. If the
element is not found in the set, it is prepended to it, which is an O(1)
operation.

delElem:
O(n). The set (of size n) is looped through to find the item. In the worst
case, the element is not in the list so that the function has to loop through
all of the items in the set.

isElem:
The same complexity as the List.mem function, which I assume to be O(n).
Regarding the simplicity of the implementation, i assume that my reasoning
is self-explanatory here.

union:
O(n * m), given n = size(a) and m = size(b). The function loops through the
entire set b (m iterations). For each iteration, the List.mem function is used
on a (O(n)). Together, this gives O(n * m).

intersection:
O(n * m + m^2). First b_in_a is calculated, which is O(n * m) (see 'union' above).
Given that x = size(b_in_a), the part that filters elements from b_in_a that are
not in b is O(x * m). In worst case, x = m, resulting in:
O(n * m + x * m) = O(n * m + m^2).