implement Dict/Set completely in roc

This commit is contained in:
Folkert 2022-07-13 00:35:20 +02:00
parent d889f1fda9
commit f9d8e01561
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
6 changed files with 124 additions and 38 deletions

View file

@ -1,5 +1,6 @@
interface Dict interface Dict
exposes [ exposes [
Dict,
empty, empty,
single, single,
get, get,
@ -10,12 +11,11 @@ interface Dict
contains, contains,
keys, keys,
values, values,
union, insertAll, keepShared, removeAll,
intersection,
difference,
] ]
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
Result.{ Result },
] ]
## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you can associate keys with values. ## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you can associate keys with values.
@ -66,34 +66,95 @@ interface Dict
## When comparing two dictionaries for equality, they are `==` only if their both their contents and their ## When comparing two dictionaries for equality, they are `==` only if their both their contents and their
## orderings match. This preserves the property that if `dict1 == dict2`, you should be able to rely on ## orderings match. This preserves the property that if `dict1 == dict2`, you should be able to rely on
## `fn dict1 == fn dict2` also being `True`, even if `fn` relies on the dictionary's ordering. ## `fn dict1 == fn dict2` also being `True`, even if `fn` relies on the dictionary's ordering.
Dict k v := List [Pair k v]
## An empty dictionary. ## An empty dictionary.
empty : Dict k v empty : Dict k v
empty = @Dict []
get : Dict k v, k -> Result v [KeyNotFound]* get : Dict k v, k -> Result v [KeyNotFound]*
get = \dict, key -> get = \@Dict list, needle ->
result = getLowlevel dict key when List.find list (\Pair key _ -> key == needle) is
Ok (Pair _ v) ->
Ok v
when result.flag is Err NotFound ->
True -> Ok result.value Err KeyNotFound
False -> Err KeyNotFound
getLowlevel : Dict k v, k -> { flag : Bool, value : v }
walk : Dict k v, state, (state, k, v -> state) -> state walk : Dict k v, state, (state, k, v -> state) -> state
walk = \@Dict list, initialState, transform ->
List.walk list initialState (\state, Pair k v -> transform state k v)
insert : Dict k v, k, v -> Dict k v insert : Dict k v, k, v -> Dict k v
insert = \@Dict list, k, v ->
when List.findIndex list (\Pair key _ -> key == k) is
Err NotFound ->
insertFresh (@Dict list) k v
Ok index ->
list
|> List.set index (Pair k v)
|> @Dict
len : Dict k v -> Nat len : Dict k v -> Nat
len = \@Dict list ->
List.len list
remove : Dict k v, k -> Dict k v remove : Dict k v, k -> Dict k v
remove = \@Dict list, key ->
when List.findIndex list (\Pair k _ -> k == key) is
Err NotFound ->
@Dict list
Ok index ->
lastIndex = List.len list - 1
list
|> List.swap index lastIndex
|> List.dropLast
|> @Dict
contains : Dict k v, k -> Bool contains : Dict k v, k -> Bool
contains = \@Dict list, needle ->
when List.find list (\Pair key _val -> key == needle) is
Ok _ -> True
Err _ -> False
single : k, v -> Dict k v single : k, v -> Dict k v
single = \key, value -> single = \key, value ->
Dict.empty @Dict [Pair key value]
|> Dict.insert key value
## Returns a [List] of the dictionary's keys. ## Returns a [List] of the dictionary's keys.
keys : Dict k v -> List k keys : Dict k v -> List k
keys = \@Dict list ->
List.map list (\Pair k _ -> k)
## Returns a [List] of the dictionary's values. ## Returns a [List] of the Dict's values
values : Dict k v -> List v values : Dict k v -> List v
union : Dict k v, Dict k v -> Dict k v values = \@Dict list ->
intersection : Dict k v, Dict k v -> Dict k v List.map list (\Pair _ v -> v)
difference : Dict k v, Dict k v -> Dict k v
# union : Dict k v, Dict k v -> Dict k v
insertAll : Dict k v, Dict k v -> Dict k v
insertAll = \xs, @Dict ys ->
List.walk ys xs (\state, Pair k v -> Dict.insert state k v)
# intersection : Dict k v, Dict k v -> Dict k v
keepShared : Dict k v, Dict k v -> Dict k v
keepShared = \Dict xs, ys ->
List.keepIf xs (\Pair k _ -> Dict.contains ys k)
|> @Dict
# difference : Dict k v, Dict k v -> Dict k v
removeAll : Dict k v, Dict k v -> Dict k v
removeAll = \xs, @Dict ys ->
List.walk ys xs (\state, Pair k _ -> Dict.remove state k)
## Internal helper function to insert a new association
##
## Precondition: `k` should not exist in the Dict yet.
insertFresh : Dict k v, k, v -> Dict k v
insertFresh = \@Dict list, k, v ->
list
|> List.append (Pair k v)
|> @Dict

View file

@ -1,5 +1,6 @@
interface Set interface Set
exposes [ exposes [
Set,
empty, empty,
single, single,
walk, walk,
@ -13,24 +14,41 @@ interface Set
intersection, intersection,
difference, difference,
] ]
imports [List, Bool.{ Bool }, Dict.{ values }] imports [List, Bool.{ Bool }, Dict.{ Dict }]
Set k := Dict.Dict k {}
fromDict : Dict k {} -> Set k
fromDict = \dict -> @Set dict
toDict : Set k -> Dict k {}
toDict = \@Set dict -> dict
## An empty set. ## An empty set.
empty : Set k empty : Set k
empty = fromDict Dict.empty
single : k -> Set k single : k -> Set k
single = \key ->
Dict.single key {}
## Make sure never to insert a *NaN* to a [Set]! Because *NaN* is defined to be ## Make sure never to insert a *NaN* to a [Set]! Because *NaN* is defined to be
## unequal to *NaN*, adding a *NaN* results in an entry that can never be ## unequal to *NaN*, adding a *NaN* results in an entry that can never be
## retrieved or removed from the [Set]. ## retrieved or removed from the [Set].
insert : Set k, k -> Set k insert : Set k, k -> Set k
insert = \@Set dict, key ->
dict
|> Dict.insert key {}
|> @Set
len : Set k -> Nat len : Set k -> Nat
len = \set -> len = \@Set dict ->
set Dict.len dict
|> Set.toDict
|> Dict.len
## Drops the given element from the set. ## Drops the given element from the set.
remove : Set k, k -> Set k remove : Set k, k -> Set k
remove = \@Set dict, key ->
@Set (Dict.remove key dict)
contains : Set k, k -> Bool contains : Set k, k -> Bool
contains = \set, key -> contains = \set, key ->
@ -38,15 +56,26 @@ contains = \set, key ->
|> Set.toDict |> Set.toDict
|> Dict.contains key |> Dict.contains key
# toList = \set -> Dict.keys (toDict set)
toList : Set k -> List k toList : Set k -> List k
toList = \@Set dict ->
Dict.keys dict
fromList : List k -> Set k fromList : List k -> Set k
fromList = \list ->
initial = (List.withCapacity (List.len list))
List.walk list initial \set, key -> Set.insert set key
union : Set k, Set k -> Set k union : Set k, Set k -> Set k
intersection : Set k, Set k -> Set k union = \@Set dict1, @Set dict2 ->
difference : Set k, Set k -> Set k @Set (Dict.insertAll dict1 dict2)
toDict : Set k -> Dict k {} intersection : Set k, Set k -> Set k
intersection = \@Set dict1, @Set dict2 ->
@Set (Dict.keepShared dict1 dict2)
difference : Set k, Set k -> Set k
difference = \@Set dict1, @Set dict2 ->
@Set (Dict.removeAll dict1 dict2)
walk : Set k, state, (state, k -> state) -> state walk : Set k, state, (state, k -> state) -> state
walk = \set, state, step -> walk = \set, state, step ->

View file

@ -328,15 +328,7 @@ pub fn canonicalize_module_defs<'a>(
panic!("TODO gracefully handle shadowing in imports.") panic!("TODO gracefully handle shadowing in imports.")
} }
} }
} else if [ } else if [Symbol::LIST_LIST, Symbol::STR_STR, Symbol::BOX_BOX_TYPE].contains(&symbol) {
Symbol::LIST_LIST,
Symbol::STR_STR,
Symbol::DICT_DICT,
Symbol::SET_SET,
Symbol::BOX_BOX_TYPE,
]
.contains(&symbol)
{
// These are not aliases but Apply's and we make sure they are always in scope // These are not aliases but Apply's and we make sure they are always in scope
} else { } else {
// This is a type alias or ability // This is a type alias or ability

View file

@ -4340,13 +4340,16 @@ fn canonicalize_and_constrain<'a>(
.into_iter() .into_iter()
.map(|(k, v)| (k, (true, v))) .map(|(k, v)| (k, (true, v)))
.collect(); .collect();
for (name, alias) in module_output.scope.aliases { for (name, alias) in module_output.scope.aliases {
match aliases.entry(name) { match aliases.entry(name) {
Occupied(_) => { Occupied(_) => {
// do nothing // do nothing
} }
Vacant(vacant) => { Vacant(vacant) => {
if !name.is_builtin() || name.module_id() == ModuleId::ENCODE { if name == Symbol::DICT_DICT {
vacant.insert((false, alias));
} else if !name.is_builtin() || name.module_id() == ModuleId::ENCODE {
vacant.insert((false, alias)); vacant.insert((false, alias));
} }
} }

View file

@ -1301,7 +1301,7 @@ define_builtins! {
9 RESULT_AFTER_ERR: "afterErr" 9 RESULT_AFTER_ERR: "afterErr"
} }
7 DICT: "Dict" => { 7 DICT: "Dict" => {
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias 0 DICT_DICT: "Dict" // the Dict.Dict type alias
1 DICT_EMPTY: "empty" 1 DICT_EMPTY: "empty"
2 DICT_SINGLE: "single" 2 DICT_SINGLE: "single"
3 DICT_GET: "get" 3 DICT_GET: "get"
@ -1322,7 +1322,7 @@ define_builtins! {
15 DICT_GET_LOWLEVEL: "getLowlevel" 15 DICT_GET_LOWLEVEL: "getLowlevel"
} }
8 SET: "Set" => { 8 SET: "Set" => {
0 SET_SET: "Set" imported // the Set.Set type alias 0 SET_SET: "Set" // the Set.Set type alias
1 SET_EMPTY: "empty" 1 SET_EMPTY: "empty"
2 SET_SINGLE: "single" 2 SET_SINGLE: "single"
3 SET_LEN: "len" 3 SET_LEN: "len"

View file

@ -314,7 +314,8 @@ impl Aliases {
let (typ, delayed_variables, &mut kind) = let (typ, delayed_variables, &mut kind) =
match self.aliases.iter_mut().find(|(s, _, _, _)| *s == symbol) { match self.aliases.iter_mut().find(|(s, _, _, _)| *s == symbol) {
None => internal_error!( None => internal_error!(
"Alias not registered in delayed aliases! {:?}", "Alias {:?} not registered in delayed aliases! {:?}",
symbol,
&self.aliases &self.aliases
), ),
Some((_, typ, delayed_variables, kind)) => (typ, delayed_variables, kind), Some((_, typ, delayed_variables, kind)) => (typ, delayed_variables, kind),