Weaken records in let-bindings

This change also means we must update the interface of `Dict.empty` and
`Set.empty` from

```
Dict.empty : Dict k v
```

to

```
Dict.empty : {} -> Dict k v
```
This commit is contained in:
Ayaz Hafiz 2023-01-12 10:48:09 -06:00 committed by Folkert
parent 61ac9bf9b2
commit b8712bcb30
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
9 changed files with 70 additions and 75 deletions

View file

@ -47,7 +47,7 @@ interface Dict
## its population as the associated value.
##
## populationByCity =
## Dict.empty
## Dict.empty {}
## |> Dict.insert "London" 8_961_989
## |> Dict.insert "Philadelphia" 1_603_797
## |> Dict.insert "Shanghai" 24_870_895
@ -100,8 +100,8 @@ Dict k v := {
} | k has Hash & Eq
## Return an empty dictionary.
empty : Dict k v | k has Hash & Eq
empty =
empty : {} -> Dict k v | k has Hash & Eq
empty = \{} ->
@Dict {
metadata: List.repeat emptySlot 8,
dataIndices: List.repeat 0 8,
@ -122,16 +122,16 @@ capacity = \@Dict { dataIndices } ->
withCapacity : Nat -> Dict k v | k has Hash & Eq
withCapacity = \_ ->
# TODO: power of 2 * 8 and actual implementation
empty
empty {}
## Returns a dictionary containing the key and value provided as input.
##
## expect
## Dict.single "A" "B"
## |> Bool.isEq (Dict.insert Dict.empty "A" "B")
## |> Bool.isEq (Dict.insert (Dict.empty {}) "A" "B")
single : k, v -> Dict k v | k has Hash & Eq
single = \k, v ->
insert empty k v
insert (empty {}) k v
## Returns dictionary with the keys and values specified by the input [List].
##
@ -144,12 +144,12 @@ single = \k, v ->
fromList : List (T k v) -> Dict k v | k has Hash & Eq
fromList = \data ->
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap.
List.walk data empty (\dict, T k v -> insert dict k v)
List.walk data (empty {}) (\dict, T k v -> insert dict k v)
## Returns the number of values in the dictionary.
##
## expect
## Dict.empty
## Dict.empty {}
## |> Dict.insert "One" "A Song"
## |> Dict.insert "Two" "Candy Canes"
## |> Dict.insert "Three" "Boughs of Holly"
@ -166,7 +166,7 @@ clear = \@Dict { metadata, dataIndices, data } ->
# Only clear large allocations.
if cap > 128 * 8 then
empty
empty {}
else
@Dict {
metadata: List.map metadata (\_ -> emptySlot),
@ -182,7 +182,7 @@ clear = \@Dict { metadata, dataIndices, data } ->
## initial `state` value provided for the first call.
##
## expect
## Dict.empty
## Dict.empty {}
## |> Dict.insert "Apples" 12
## |> Dict.insert "Orange" 24
## |> Dict.walk 0 (\count, _, qty -> count + qty)
@ -210,7 +210,7 @@ walkUntil = \@Dict { data }, initialState, transform ->
## will return [Ok value], otherwise return [Err KeyNotFound].
##
## dictionary =
## Dict.empty
## Dict.empty {}
## |> Dict.insert 1 "Apple"
## |> Dict.insert 2 "Orange"
##
@ -239,7 +239,7 @@ get = \@Dict { metadata, dataIndices, data }, key ->
## Check if the dictionary has a value for a specified key.
##
## expect
## Dict.empty
## Dict.empty {}
## |> Dict.insert 1234 "5678"
## |> Dict.contains 1234
## |> Bool.isEq Bool.true
@ -263,7 +263,7 @@ contains = \@Dict { metadata, dataIndices, data }, key ->
## Insert a value into the dictionary at a specified key.
##
## expect
## Dict.empty
## Dict.empty {}
## |> Dict.insert "Apples" 12
## |> Dict.get "Apples"
## |> Bool.isEq (Ok 12)
@ -307,7 +307,7 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
## Remove a value from the dictionary for a specified key.
##
## expect
## Dict.empty
## Dict.empty {}
## |> Dict.insert "Some" "Value"
## |> Dict.remove "Some"
## |> Dict.len
@ -352,9 +352,9 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
## Missing -> Present Bool.false
## Present value -> if value then Missing else Present Bool.true
##
## expect Dict.update Dict.empty "a" alterValue == Dict.single "a" Bool.false
## expect Dict.update (Dict.empty {}) "a" alterValue == Dict.single "a" Bool.false
## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty {}
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v | k has Hash & Eq
update = \dict, key, alter ->
# TODO: look into optimizing by merging substeps and reducing lookups.
@ -457,7 +457,7 @@ keepShared : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
keepShared = \xs, ys ->
walk
xs
empty
(empty {})
(\state, k, v ->
if contains ys k then
insert state k v
@ -688,7 +688,7 @@ h2 = \hashKey ->
expect
val =
empty
empty {}
|> insert "foo" "bar"
|> get "foo"
@ -696,7 +696,7 @@ expect
expect
val =
empty
empty {}
|> insert "foo" "bar"
|> insert "foo" "baz"
|> get "foo"
@ -705,20 +705,20 @@ expect
expect
val =
empty
empty {}
|> insert "foo" "bar"
|> get "bar"
val == Err KeyNotFound
expect
empty
empty {}
|> insert "foo" {}
|> contains "foo"
expect
dict =
empty
empty {}
|> insert "foo" {}
|> insert "bar" {}
|> insert "baz" {}
@ -746,7 +746,7 @@ expect
# Reach capacity, no rehash.
expect
val =
empty
empty {}
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
@ -760,7 +760,7 @@ expect
expect
dict =
empty
empty {}
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
@ -780,7 +780,7 @@ expect
# Force rehash.
expect
val =
empty
empty {}
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
@ -795,7 +795,7 @@ expect
expect
dict =
empty
empty {}
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
@ -815,7 +815,7 @@ expect
&& (get dict "h" == Ok 7)
expect
empty
empty {}
|> insert "Some" "Value"
|> remove "Some"
|> len
@ -823,7 +823,7 @@ expect
# Makes sure a Dict with Nat keys works
expect
empty
empty {}
|> insert 7nat "Testing"
|> get 7
|> Bool.isEq (Ok "Testing")

View file

@ -43,9 +43,9 @@ isEq = \xs, ys ->
else
Break Bool.false
## An empty set.
empty : Set k | k has Hash & Eq
empty = @Set Dict.empty
## Creates a new empty set.
empty : {} -> Set k | k has Hash & Eq
empty = \{} -> @Set (Dict.empty {})
single : k -> Set k | k has Hash & Eq
single = \key ->
@ -58,14 +58,14 @@ insert = \@Set dict, key ->
# Inserting a duplicate key has no effect.
expect
actual =
empty
empty {}
|> insert "foo"
|> insert "bar"
|> insert "foo"
|> insert "baz"
expected =
empty
empty {}
|> insert "foo"
|> insert "bar"
|> insert "baz"
@ -79,7 +79,7 @@ len = \@Set dict ->
# Inserting a duplicate key has no effect on length.
expect
actual =
empty
empty {}
|> insert "foo"
|> insert "bar"
|> insert "foo"

View file

@ -3833,13 +3833,13 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool {
| RunLowLevel { .. }
| ForeignCall { .. }
| EmptyRecord
| Expr::Record { .. }
=> {
return false
}
// TODO(weakening)
Var(_, _)
| AbilityMember(_, _, _)
| Expr::Record { .. }
| Crash { .. }
| Access { .. }
| Accessor(_)

View file

@ -8476,10 +8476,10 @@ mod solve_expr {
main =
s1 : Set U8
s1 = Set.empty
s1 = Set.empty {}
s2 : Set Str
s2 = Set.empty
s2 = Set.empty {}
Bool.isEq s1 s1 && Bool.isEq s2 s2
# ^^^^^^^^^ ^^^^^^^^^

View file

@ -1,18 +1,18 @@
procedure Dict.1 ():
let Dict.520 : List {[], []} = Array [];
let Dict.527 : U64 = 0i64;
let Dict.528 : U64 = 8i64;
let Dict.521 : List U64 = CallByName List.11 Dict.527 Dict.528;
let Dict.524 : I8 = CallByName Dict.34;
let Dict.525 : U64 = 8i64;
let Dict.522 : List I8 = CallByName List.11 Dict.524 Dict.525;
let Dict.523 : U64 = 0i64;
let Dict.519 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.520, Dict.521, Dict.522, Dict.523};
ret Dict.519;
procedure Dict.1 (Dict.518):
let Dict.521 : List {[], []} = Array [];
let Dict.528 : U64 = 0i64;
let Dict.529 : U64 = 8i64;
let Dict.522 : List U64 = CallByName List.11 Dict.528 Dict.529;
let Dict.525 : I8 = CallByName Dict.34;
let Dict.526 : U64 = 8i64;
let Dict.523 : List I8 = CallByName List.11 Dict.525 Dict.526;
let Dict.524 : U64 = 0i64;
let Dict.520 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.521, Dict.522, Dict.523, Dict.524};
ret Dict.520;
procedure Dict.34 ():
let Dict.526 : I8 = -128i64;
ret Dict.526;
let Dict.527 : I8 = -128i64;
ret Dict.527;
procedure Dict.4 (Dict.507):
let Dict.85 : U64 = StructAtIndex 3 Dict.507;
@ -82,6 +82,7 @@ procedure Num.24 (#Attr.2, #Attr.3):
ret Num.259;
procedure Test.0 ():
let Test.2 : {List {[], []}, List U64, List I8, U64} = CallByName Dict.1;
let Test.3 : {} = Struct {};
let Test.2 : {List {[], []}, List U64, List I8, U64} = CallByName Dict.1 Test.3;
let Test.1 : U64 = CallByName Dict.4 Test.2;
ret Test.1;

View file

@ -0,0 +1,13 @@
procedure Test.1 ():
let Test.7 : U8 = 1i64;
let Test.8 : U8 = 2i64;
let Test.6 : {U8, U8} = Struct {Test.7, Test.8};
ret Test.6;
procedure Test.0 ():
let Test.9 : {U8, U8} = CallByName Test.1;
let Test.3 : U8 = StructAtIndex 0 Test.9;
let Test.5 : {U8, U8} = CallByName Test.1;
let Test.4 : U8 = StructAtIndex 1 Test.5;
let Test.2 : List U8 = Array [Test.3, Test.4];
ret Test.2;

View file

@ -1,19 +0,0 @@
procedure Test.1 ():
let Test.11 : I64 = 2i64;
let Test.12 : U8 = 1i64;
let Test.10 : {I64, U8} = Struct {Test.11, Test.12};
ret Test.10;
procedure Test.1 ():
let Test.7 : I64 = 1i64;
let Test.8 : U8 = 2i64;
let Test.6 : {I64, U8} = Struct {Test.7, Test.8};
ret Test.6;
procedure Test.0 ():
let Test.9 : {I64, U8} = CallByName Test.1;
let Test.3 : U8 = StructAtIndex 1 Test.9;
let Test.5 : {I64, U8} = CallByName Test.1;
let Test.4 : U8 = StructAtIndex 1 Test.5;
let Test.2 : List U8 = Array [Test.3, Test.4];
ret Test.2;

View file

@ -385,7 +385,7 @@ fn when_on_two_values() {
#[mono_test]
fn dict() {
r#"
Dict.len Dict.empty
Dict.len (Dict.empty {})
"#
}
@ -1273,7 +1273,7 @@ fn aliased_polymorphic_closure() {
}
#[mono_test]
fn issue_2535_polymorphic_fields_referenced_in_list() {
fn issue_2535_let_weakened_fields_referenced_in_list() {
indoc!(
r#"
app "test" provides [nums] to "./platform"

View file

@ -4777,7 +4777,7 @@ Tab characters are not allowed."###,
app "dict" imports [ Dict ] provides [main] to "./platform"
myDict : Dict.Dict Num.I64 Str
myDict = Dict.insert Dict.empty "foo" 42
myDict = Dict.insert (Dict.empty {}) "foo" 42
main = myDict
"#
@ -4788,8 +4788,8 @@ Tab characters are not allowed."###,
Something is off with the body of the `myDict` definition:
3 myDict : Dict.Dict Num.I64 Str
4 myDict = Dict.insert Dict.empty "foo" 42
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 myDict = Dict.insert (Dict.empty {}) "foo" 42
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This `insert` call produces: