mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
implement Dict/Set completely in roc
This commit is contained in:
parent
d889f1fda9
commit
f9d8e01561
6 changed files with 124 additions and 38 deletions
|
@ -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
|
||||||
|
|
|
@ -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 ->
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue