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),
size : Nat,
} | 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.
## ```
@ -747,6 +771,71 @@ expect
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
val =
empty {}

View file

@ -20,19 +20,19 @@ interface Set
Bool.{ Bool, Eq },
Dict.{ Dict },
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))
## 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 [
Eq {
isEq,
},
Hash {
hash: hashSet,
},
]
isEq : Set k, Set k -> Bool | k has Hash & Eq
@ -46,6 +46,9 @@ isEq = \xs, ys ->
else
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`.
## ```
## emptySet = Set.empty {}
@ -353,3 +356,26 @@ expect
|> insert 9
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):
let Dict.518 : List {[], []} = Array [];
let Dict.525 : U64 = 0i64;
let Dict.526 : U64 = 8i64;
let Dict.519 : List U64 = CallByName List.11 Dict.525 Dict.526;
let Dict.522 : I8 = CallByName Dict.34;
let Dict.523 : U64 = 8i64;
let Dict.520 : List I8 = CallByName List.11 Dict.522 Dict.523;
let Dict.521 : U64 = 0i64;
let Dict.517 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.518, Dict.519, Dict.520, Dict.521};
ret Dict.517;
procedure Dict.1 (Dict.536):
let Dict.546 : List {[], []} = Array [];
let Dict.553 : U64 = 0i64;
let Dict.554 : U64 = 8i64;
let Dict.547 : List U64 = CallByName List.11 Dict.553 Dict.554;
let Dict.550 : I8 = CallByName Dict.36;
let Dict.551 : U64 = 8i64;
let Dict.548 : List I8 = CallByName List.11 Dict.550 Dict.551;
let Dict.549 : U64 = 0i64;
let Dict.545 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.546, Dict.547, Dict.548, Dict.549};
ret Dict.545;
procedure Dict.34 ():
let Dict.524 : I8 = -128i64;
ret Dict.524;
procedure Dict.36 ():
let Dict.552 : I8 = -128i64;
ret Dict.552;
procedure Dict.4 (Dict.504):
let Dict.85 : U64 = StructAtIndex 3 Dict.504;
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.504;
procedure Dict.4 (Dict.543):
let Dict.97 : U64 = StructAtIndex 3 Dict.543;
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.543;
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;
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.504;
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.543;
dec #Derived_gen.0;
ret Dict.85;
ret Dict.97;
procedure List.11 (List.115, 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 flex_able: Vec<Variable>,
pub rigid_able: Vec<Variable>,
pub translations: Vec<(Variable, Variable)>,
pub registered: Vec<Variable>,
}
@ -5320,7 +5319,6 @@ struct CopyImportEnv<'a> {
rigid: Vec<Variable>,
flex_able: Vec<Variable>,
rigid_able: Vec<Variable>,
translations: Vec<(Variable, Variable)>,
registered: Vec<Variable>,
}
@ -5348,7 +5346,6 @@ pub fn copy_import_to(
rigid: Vec::new(),
flex_able: Vec::new(),
rigid_able: Vec::new(),
translations: Vec::new(),
registered: Vec::new(),
};
@ -5362,7 +5359,6 @@ pub fn copy_import_to(
rigid,
flex_able,
rigid_able,
translations,
registered,
target: _,
bookkeep_unspecialized_lambda_sets: _,
@ -5374,7 +5370,6 @@ pub fn copy_import_to(
rigid,
flex_able,
rigid_able,
translations,
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 new_name_index = SubsIndex::push_new(&mut env.target.field_names, name);
env.target
.set(copy, make_descriptor(RigidVar(new_name_index)));
// If we are copying the import as generalized, we can keep it as rigid.
// 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.translations.push((var, 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.target.set(
copy,
make_descriptor(RigidAbleVar(new_name_index, new_abilities)),
);
// If we are copying the import as generalized, we can keep it as rigid.
// 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() {
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.translations.push((var, copy));
copy
}

View file

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