chharvey / counterpoint

A robust programming language.
GNU Affero General Public License v3.0
2 stars 0 forks source link

Collection Literals — Sets #76

Closed chharvey closed 3 years ago

chharvey commented 3 years ago

Sets are dynamic-sized unordered lists of values.

let seasons: obj = {'winter', 'spring', 'summer', 'fall'};

Sets cannot contain identical (via ===) elements. The set {'hello', 'hello'} only contains 1 element.

Sets may have several elements that are un-identical but “equal”.

let x: [str] = ['hello'];
let y: [str] = ['hello'];
let set: obj = {0.0, -0.0, x, y};

Even though 0.0 == -0.0 and x == y, this set has four elements.

Sets are considered equal (via ==) iff they have equal elements. The order of elements in a set is not relevant.

let x: obj = {'hello', 'world'};
let y: obj = {'world', 'hello'};
x === y; %== false
x == y;  %== true

Set elements can be accessed via bracket accessor notation. If the expression in the brackets exists in the set, it is produced. (In a way, think of a Set as a Mapping whose keys and corresponding values are the same). If the compiler can determine that the value does not exist in the set, a VoidError is thrown; otherwise it’s a runtime error. An expression of an incorrect type is a TypeError.

{'hello', 'world'}.['hello']; %== 'hello'
{'hello', 'world'}.['hi'];    % compile-time VoidError or runtime error
{'hello', 'world'}.[42];      % compile-time TypeError

Optional access and claim access as normal.

{'hello', 'world'}?.['hello']; %== 'hello'
{'hello', 'world'}?.['hi'];    %== null
{'hello', 'world'}!.['hello']; %== 'hello'
{'hello', 'world'}!.['hi'];    %: str      % claims type is non-null string, but results in runtime error
chharvey commented 1 year ago

This comment updates and supercedes parts of the previous comment.

Set access can be done via bracket accessor notation, where the value produced is a boolean indicating whether the element is in the set.

let s: Set.<str> = {'hello', 'world'};
s.['hello']; %== true
s.['hi'];    %== false
s.[42];      %> TypeError

A TypeError is reported at compile-time if the type of the accessed element does not match the set’s invariant. In the example above, s is of type Set.<str> (the set’s invariant is type str), so accessing .[42] produces a type error since 42 is not a string. In other words, since it’s not even possible for an integer to exist in the set at all, compilation is stopped.

Optional access and claim access are irrelevant, since the returned value will always be a boolean.

s?.['hello']; %== true
s?.['hi'];    %== false
s?.[42];      %> TypeError
s!.['hello']; %== true
s!.['hi'];    %== false
s!.[42];      %> TypeError

When using bracket accessor notation to mutate a mutable set, setting the value true will add the element to the set (if it’s not already added), and setting false will remove the element (if it’s not already removed). Type errors are as usual.

let s: mutable str{} = {'hello', 'world'};
set s.['hi'] = true;
s; %== {'hello', 'world', 'hi'}
set s.['hello'] = false;
s; %== {'world', 'hi'}
set s.[42] = true; %> TypeError
set s.['world'] = anything_but_a_boolean; %> TypeError

In a way, set access can be treated like map access where the map’s keys (antecedents) are elements of the set and the map’s values (consequents) are boolean.