Merge pull request #5438 from roc-lang/dict-set-hash-eq

add Hash and Eq to Dict and Set
This commit is contained in:
Brendan Hansknecht 2023-05-25 00:38:20 +00:00 committed by GitHub
commit 8ecbd8c071
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 171 additions and 47 deletions

View file

@ -98,6 +98,30 @@ Dict k v := {
data : List (T k v), data : List (T k v),
size : Nat, size : Nat,
} | k has Hash & Eq } | k has Hash & Eq
has [
Eq {
isEq,
},
Hash {
hash: hashDict,
},
]
isEq : Dict k v, Dict k v -> Bool | k has Hash & Eq, v has Eq
isEq = \xs, ys ->
if len xs != len ys then
Bool.false
else
walkUntil xs Bool.true \_, k, xVal ->
when get ys k is
Ok yVal if yVal == xVal ->
Continue Bool.true
_ ->
Break Bool.false
hashDict : hasher, Dict k v -> hasher | k has Hash & Eq, v has Hash, hasher has Hasher
hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk
## Return an empty dictionary. ## Return an empty dictionary.
## ``` ## ```
@ -747,6 +771,71 @@ expect
val == Ok "bar" val == Ok "bar"
expect
dict1 =
empty {}
|> insert 1 "bar"
|> insert 2 "baz"
dict2 =
empty {}
|> insert 2 "baz"
|> insert 1 "bar"
dict1 == dict2
expect
dict1 =
empty {}
|> insert 1 "bar"
|> insert 2 "baz"
dict2 =
empty {}
|> insert 1 "bar"
|> insert 2 "baz!"
dict1 != dict2
expect
inner1 =
empty {}
|> insert 1 "bar"
|> insert 2 "baz"
inner2 =
empty {}
|> insert 2 "baz"
|> insert 1 "bar"
outer =
empty {}
|> insert inner1 "wrong"
|> insert inner2 "right"
get outer inner1 == Ok "right"
expect
inner1 =
empty {}
|> insert 1 "bar"
|> insert 2 "baz"
inner2 =
empty {}
|> insert 2 "baz"
|> insert 1 "bar"
outer1 =
empty {}
|> insert inner1 "val"
outer2 =
empty {}
|> insert inner2 "val"
outer1 == outer2
expect expect
val = val =
empty {} empty {}

View file

@ -20,19 +20,19 @@ interface Set
Bool.{ Bool, Eq }, Bool.{ Bool, Eq },
Dict.{ Dict }, Dict.{ Dict },
Num.{ Nat }, Num.{ Nat },
Hash.{ Hash }, Hash.{ Hash, Hasher },
] ]
# We should have this line above the next has.
# It causes the formatter to fail currently.
# | k has Hash & Eq
## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) ## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type))
## type which stores a collection of unique values, without any ordering ## type which stores a collection of unique values, without any ordering
Set k := Dict.Dict k {} Set k := Dict.Dict k {} | k has Hash & Eq
has [ has [
Eq { Eq {
isEq, isEq,
}, },
Hash {
hash: hashSet,
},
] ]
isEq : Set k, Set k -> Bool | k has Hash & Eq isEq : Set k, Set k -> Bool | k has Hash & Eq
@ -46,6 +46,9 @@ isEq = \xs, ys ->
else else
Break Bool.false Break Bool.false
hashSet : hasher, Set k -> hasher | k has Hash & Eq, hasher has Hasher
hashSet = \hasher, @Set inner -> Hash.hash hasher inner
## Creates a new empty `Set`. ## Creates a new empty `Set`.
## ``` ## ```
## emptySet = Set.empty {} ## emptySet = Set.empty {}
@ -353,3 +356,26 @@ expect
|> insert 9 |> insert 9
x == fromList (toList x) x == fromList (toList x)
expect
orderOne : Set Nat
orderOne =
single 1
|> insert 2
orderTwo : Set Nat
orderTwo =
single 2
|> insert 1
wrapperOne : Set (Set Nat)
wrapperOne =
single orderOne
|> insert orderTwo
wrapperTwo : Set (Set Nat)
wrapperTwo =
single orderTwo
|> insert orderOne
wrapperOne == wrapperTwo

View file

@ -1,28 +1,28 @@
procedure Dict.1 (Dict.515): procedure Dict.1 (Dict.536):
let Dict.518 : List {[], []} = Array []; let Dict.546 : List {[], []} = Array [];
let Dict.525 : U64 = 0i64; let Dict.553 : U64 = 0i64;
let Dict.526 : U64 = 8i64; let Dict.554 : U64 = 8i64;
let Dict.519 : List U64 = CallByName List.11 Dict.525 Dict.526; let Dict.547 : List U64 = CallByName List.11 Dict.553 Dict.554;
let Dict.522 : I8 = CallByName Dict.34; let Dict.550 : I8 = CallByName Dict.36;
let Dict.523 : U64 = 8i64; let Dict.551 : U64 = 8i64;
let Dict.520 : List I8 = CallByName List.11 Dict.522 Dict.523; let Dict.548 : List I8 = CallByName List.11 Dict.550 Dict.551;
let Dict.521 : U64 = 0i64; let Dict.549 : U64 = 0i64;
let Dict.517 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.518, Dict.519, Dict.520, Dict.521}; let Dict.545 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.546, Dict.547, Dict.548, Dict.549};
ret Dict.517; ret Dict.545;
procedure Dict.34 (): procedure Dict.36 ():
let Dict.524 : I8 = -128i64; let Dict.552 : I8 = -128i64;
ret Dict.524; ret Dict.552;
procedure Dict.4 (Dict.504): procedure Dict.4 (Dict.543):
let Dict.85 : U64 = StructAtIndex 3 Dict.504; let Dict.97 : U64 = StructAtIndex 3 Dict.543;
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.504; let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.543;
dec #Derived_gen.2; dec #Derived_gen.2;
let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.504; let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.543;
dec #Derived_gen.1; dec #Derived_gen.1;
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.504; let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.543;
dec #Derived_gen.0; dec #Derived_gen.0;
ret Dict.85; ret Dict.97;
procedure List.11 (List.115, List.116): procedure List.11 (List.115, List.116):
let List.495 : List I8 = CallByName List.68 List.116; let List.495 : List I8 = CallByName List.68 List.116;

View file

@ -5300,7 +5300,6 @@ pub struct CopiedImport {
pub rigid: Vec<Variable>, pub rigid: Vec<Variable>,
pub flex_able: Vec<Variable>, pub flex_able: Vec<Variable>,
pub rigid_able: Vec<Variable>, pub rigid_able: Vec<Variable>,
pub translations: Vec<(Variable, Variable)>,
pub registered: Vec<Variable>, pub registered: Vec<Variable>,
} }
@ -5320,7 +5319,6 @@ struct CopyImportEnv<'a> {
rigid: Vec<Variable>, rigid: Vec<Variable>,
flex_able: Vec<Variable>, flex_able: Vec<Variable>,
rigid_able: Vec<Variable>, rigid_able: Vec<Variable>,
translations: Vec<(Variable, Variable)>,
registered: Vec<Variable>, registered: Vec<Variable>,
} }
@ -5348,7 +5346,6 @@ pub fn copy_import_to(
rigid: Vec::new(), rigid: Vec::new(),
flex_able: Vec::new(), flex_able: Vec::new(),
rigid_able: Vec::new(), rigid_able: Vec::new(),
translations: Vec::new(),
registered: Vec::new(), registered: Vec::new(),
}; };
@ -5362,7 +5359,6 @@ pub fn copy_import_to(
rigid, rigid,
flex_able, flex_able,
rigid_able, rigid_able,
translations,
registered, registered,
target: _, target: _,
bookkeep_unspecialized_lambda_sets: _, bookkeep_unspecialized_lambda_sets: _,
@ -5374,7 +5370,6 @@ pub fn copy_import_to(
rigid, rigid,
flex_able, flex_able,
rigid_able, rigid_able,
translations,
registered, registered,
} }
}; };
@ -5668,13 +5663,21 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
let name = env.source.field_names[name_index.index as usize].clone(); let name = env.source.field_names[name_index.index as usize].clone();
let new_name_index = SubsIndex::push_new(&mut env.target.field_names, name); let new_name_index = SubsIndex::push_new(&mut env.target.field_names, name);
env.target // If we are copying the import as generalized, we can keep it as rigid.
.set(copy, make_descriptor(RigidVar(new_name_index))); // Otherwise we must make it flex, as this is copying to a non-generalized site.
//
// The rigid distinction is never necessary for imports, since their types have already
// been checked completely.
let content = if max_rank.is_generalized() {
RigidVar(new_name_index)
} else {
FlexVar(Some(new_name_index))
};
env.target.set(copy, make_descriptor(content));
env.rigid.push(copy); env.rigid.push(copy);
env.translations.push((var, copy));
copy copy
} }
@ -5687,15 +5690,21 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
env.source.get_subs_slice(abilities).iter().copied(), env.source.get_subs_slice(abilities).iter().copied(),
); );
env.target.set( // If we are copying the import as generalized, we can keep it as rigid.
copy, // Otherwise we must make it flex, as this is copying to a non-generalized site.
make_descriptor(RigidAbleVar(new_name_index, new_abilities)), //
); // The rigid distinction is never necessary for imports, since their types have already
// been checked completely.
let content = if max_rank.is_generalized() {
RigidAbleVar(new_name_index, new_abilities)
} else {
FlexAbleVar(Some(new_name_index), new_abilities)
};
env.target.set(copy, make_descriptor(content));
env.rigid_able.push(copy); env.rigid_able.push(copy);
env.translations.push((var, copy));
copy copy
} }

View file

@ -3,22 +3,22 @@
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
f = \{} -> f = \{} ->
#^{-1} <1527><116>{} -<119>[[f(1)]]-> <115>[Ok <1535>{}]<79>* #^{-1} <1606><116>{} -<119>[[f(1)]]-> <115>[Ok <1614>{}]<79>*
when g {} is when g {} is
# ^ <1517><1535>{} -<1525>[[g(2)]]-> <71>[Ok <1535>{}]<101>* # ^ <1596><1614>{} -<1604>[[g(2)]]-> <71>[Ok <1614>{}]<101>*
_ -> Ok {} _ -> Ok {}
g = \{} -> g = \{} ->
#^{-1} <1517><1535>{} -<1525>[[g(2)]]-> <71>[Ok <1535>{}]<101>* #^{-1} <1596><1614>{} -<1604>[[g(2)]]-> <71>[Ok <1614>{}]<101>*
when h {} is when h {} is
# ^ <1522><1535>{} -<1530>[[h(3)]]-> <93>[Ok <1535>{}]<123>* # ^ <1601><1614>{} -<1609>[[h(3)]]-> <93>[Ok <1614>{}]<123>*
_ -> Ok {} _ -> Ok {}
h = \{} -> h = \{} ->
#^{-1} <1522><1535>{} -<1530>[[h(3)]]-> <93>[Ok <1535>{}]<123>* #^{-1} <1601><1614>{} -<1609>[[h(3)]]-> <93>[Ok <1614>{}]<123>*
when f {} is when f {} is
# ^ <1527><116>{} -<119>[[f(1)]]-> <115>[Ok <1535>{}]<79>* # ^ <1606><116>{} -<119>[[f(1)]]-> <115>[Ok <1614>{}]<79>*
_ -> Ok {} _ -> Ok {}
main = f {} main = f {}
# ^ <1537><132>{} -<135>[[f(1)]]-> <137>[Ok <1535>{}]<1536>w_a # ^ <1616><132>{} -<135>[[f(1)]]-> <137>[Ok <1614>{}]<1615>w_a