Replace Bool.isEq with Eq.isEq

This commit is contained in:
Ayaz Hafiz 2022-10-08 11:50:04 -05:00
parent 4adae7651f
commit c618ced743
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
9 changed files with 40 additions and 46 deletions

View file

@ -1,5 +1,5 @@
interface Bool interface Bool
exposes [Bool, true, false, and, or, not, isEq, isNotEq] exposes [Bool, true, false, and, or, not, isNotEq]
imports [] imports []
Bool := [True, False] Bool := [True, False]
@ -67,21 +67,6 @@ or : Bool, Bool -> Bool
## Returns `Bool.false` when given `Bool.true`, and vice versa. ## Returns `Bool.false` when given `Bool.true`, and vice versa.
not : Bool -> Bool not : Bool -> Bool
## Returns `Bool.true` if the two values are *structurally equal*, and `Bool.false` otherwise.
##
## `a == b` is shorthand for `Bool.isEq a b`
##
## Structural equality works as follows:
##
## 1. Tags are equal if they have the same tag name, and also their contents (if any) are equal.
## 2. Records are equal if all their fields are equal.
## 3. Collections ([Str], [List], [Dict], and [Set]) are equal if they are the same length, and also all their corresponding elements are equal.
## 4. [Num](Num#Num) values are equal if their numbers are equal, with one exception: if both arguments to `isEq` are *NaN*, then `isEq` returns `Bool.false`. See `Num.isNaN` for more about *NaN*.
##
## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not
## accept arguments whose types contain functions.
isEq : a, a -> Bool
## Calls [isEq] on the given values, then calls [not] on the result. ## Calls [isEq] on the given values, then calls [not] on the result.
## ##
## `a != b` is shorthand for `Bool.isNotEq a b` ## `a != b` is shorthand for `Bool.isNotEq a b`

View file

@ -19,6 +19,7 @@ interface Dict
] ]
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
Eq.{ Eq },
Result.{ Result }, Result.{ Result },
List, List,
Num.{ Nat }, Num.{ Nat },
@ -72,7 +73,9 @@ 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 `Bool.true`, even if `fn` relies on the dictionary's ordering. ## `fn dict1 == fn dict2` also being `Bool.true`, even if `fn` relies on the dictionary's ordering.
Dict k v := List [Pair k v] Dict k v := List [Pair k v] has [Eq { isEq: dictEq }]
dictEq = \@Dict l1, @Dict l2 -> l1 == l2
## An empty dictionary. ## An empty dictionary.
empty : Dict k v empty : Dict k v
@ -81,7 +84,7 @@ empty = @Dict []
withCapacity : Nat -> Dict k v withCapacity : Nat -> Dict k v
withCapacity = \n -> @Dict (List.withCapacity n) withCapacity = \n -> @Dict (List.withCapacity n)
get : Dict k v, k -> Result v [KeyNotFound]* get : Dict k v, k -> Result v [KeyNotFound]* | k has Eq
get = \@Dict list, needle -> get = \@Dict list, needle ->
when List.findFirst list (\Pair key _ -> key == needle) is when List.findFirst list (\Pair key _ -> key == needle) is
Ok (Pair _ v) -> Ok (Pair _ v) ->
@ -94,7 +97,7 @@ walk : Dict k v, state, (state, k, v -> state) -> state
walk = \@Dict list, initialState, transform -> walk = \@Dict list, initialState, transform ->
List.walk list initialState (\state, Pair k v -> transform state k v) 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 | k has Eq
insert = \@Dict list, k, v -> insert = \@Dict list, k, v ->
when List.findFirstIndex list (\Pair key _ -> key == k) is when List.findFirstIndex list (\Pair key _ -> key == k) is
Err NotFound -> Err NotFound ->
@ -109,7 +112,7 @@ len : Dict k v -> Nat
len = \@Dict list -> len = \@Dict list ->
List.len list List.len list
remove : Dict k v, k -> Dict k v remove : Dict k v, k -> Dict k v | k has Eq
remove = \@Dict list, key -> remove = \@Dict list, key ->
when List.findFirstIndex list (\Pair k _ -> k == key) is when List.findFirstIndex list (\Pair k _ -> k == key) is
Err NotFound -> Err NotFound ->
@ -128,7 +131,7 @@ update :
Dict k v, Dict k v,
k, k,
([Present v, Missing] -> [Present v, Missing]) ([Present v, Missing] -> [Present v, Missing])
-> Dict k v -> Dict k v | k has Eq
update = \dict, key, alter -> update = \dict, key, alter ->
possibleValue = possibleValue =
get dict key get dict key
@ -151,7 +154,7 @@ expect update empty "a" alterValue == single "a" Bool.false
expect update (single "a" Bool.false) "a" alterValue == single "a" Bool.true expect update (single "a" Bool.false) "a" alterValue == single "a" Bool.true
expect update (single "a" Bool.true) "a" alterValue == empty expect update (single "a" Bool.true) "a" alterValue == empty
contains : Dict k v, k -> Bool contains : Dict k v, k -> Bool | k has Eq
contains = \@Dict list, needle -> contains = \@Dict list, needle ->
step = \_, Pair key _val -> step = \_, Pair key _val ->
if key == needle then if key == needle then
@ -178,18 +181,18 @@ values = \@Dict list ->
List.map list (\Pair _ v -> v) List.map list (\Pair _ v -> v)
# union : 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 : Dict k v, Dict k v -> Dict k v | k has Eq
insertAll = \xs, @Dict ys -> insertAll = \xs, @Dict ys ->
List.walk ys xs (\state, Pair k v -> Dict.insertIfVacant state k v) List.walk ys xs (\state, Pair k v -> Dict.insertIfVacant state k v)
# intersection : Dict k v, Dict k v -> Dict k v # intersection : Dict k v, Dict k v -> Dict k v
keepShared : Dict k v, Dict k v -> Dict k v keepShared : Dict k v, Dict k v -> Dict k v | k has Eq
keepShared = \@Dict xs, ys -> keepShared = \@Dict xs, ys ->
List.keepIf xs (\Pair k _ -> Dict.contains ys k) List.keepIf xs (\Pair k _ -> Dict.contains ys k)
|> @Dict |> @Dict
# difference : Dict k v, Dict k v -> Dict k v # difference : Dict k v, Dict k v -> Dict k v
removeAll : Dict k v, Dict k v -> Dict k v removeAll : Dict k v, Dict k v -> Dict k v | k has Eq
removeAll = \xs, @Dict ys -> removeAll = \xs, @Dict ys ->
List.walk ys xs (\state, Pair k _ -> Dict.remove state k) List.walk ys xs (\state, Pair k _ -> Dict.remove state k)
@ -202,7 +205,7 @@ insertFresh = \@Dict list, k, v ->
|> List.append (Pair k v) |> List.append (Pair k v)
|> @Dict |> @Dict
insertIfVacant : Dict k v, k, v -> Dict k v insertIfVacant : Dict k v, k, v -> Dict k v | k has Eq
insertIfVacant = \dict, key, value -> insertIfVacant = \dict, key, value ->
if Dict.contains dict key then if Dict.contains dict key then
dict dict

View file

@ -6,7 +6,7 @@ interface Eq
structuralEq, structuralEq,
] ]
imports [ imports [
Bool, Bool.{ Bool },
] ]
## A type that can be compared for total equality. ## A type that can be compared for total equality.

View file

@ -34,6 +34,7 @@ interface Json
Dec, Dec,
}, },
Bool.{ Bool }, Bool.{ Bool },
Eq.{ Eq },
Result, Result,
] ]

View file

@ -66,6 +66,7 @@ interface List
] ]
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
Eq.{ Eq },
Result.{ Result }, Result.{ Result },
Num.{ Nat, Num, Int }, Num.{ Nat, Num, Int },
] ]
@ -354,7 +355,7 @@ join = \lists ->
List.walk lists (List.withCapacity totalLength) (\state, list -> List.concat state list) List.walk lists (List.withCapacity totalLength) (\state, list -> List.concat state list)
contains : List a, a -> Bool contains : List a, a -> Bool | a has Eq
contains = \list, needle -> contains = \list, needle ->
List.any list (\x -> x == needle) List.any list (\x -> x == needle)
@ -903,7 +904,7 @@ intersperse = \list, sep ->
## is considered to "start with" an empty list. ## is considered to "start with" an empty list.
## ##
## If the first list is empty, this only returns `Bool.true` if the second list is empty. ## If the first list is empty, this only returns `Bool.true` if the second list is empty.
startsWith : List elem, List elem -> Bool startsWith : List elem, List elem -> Bool | elem has Eq
startsWith = \list, prefix -> startsWith = \list, prefix ->
# TODO once we have seamless slices, verify that this wouldn't # TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compareSublists # have better performance with a function like List.compareSublists
@ -915,7 +916,7 @@ startsWith = \list, prefix ->
## is considered to "end with" an empty list. ## is considered to "end with" an empty list.
## ##
## If the first list is empty, this only returns `Bool.true` if the second list is empty. ## If the first list is empty, this only returns `Bool.true` if the second list is empty.
endsWith : List elem, List elem -> Bool endsWith : List elem, List elem -> Bool | elem has Eq
endsWith = \list, suffix -> endsWith = \list, suffix ->
# TODO once we have seamless slices, verify that this wouldn't # TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compareSublists # have better performance with a function like List.compareSublists
@ -944,7 +945,7 @@ split = \elements, userSplitIndex ->
## remaining elements after that occurrence. If the delimiter is not found, returns `Err`. ## remaining elements after that occurrence. If the delimiter is not found, returns `Err`.
## ##
## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Baz] } ## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Baz] }
splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound]* splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound]* | elem has Eq
splitFirst = \list, delimiter -> splitFirst = \list, delimiter ->
when List.findFirstIndex list (\elem -> elem == delimiter) is when List.findFirstIndex list (\elem -> elem == delimiter) is
Ok index -> Ok index ->
@ -959,7 +960,7 @@ splitFirst = \list, delimiter ->
## remaining elements after that occurrence. If the delimiter is not found, returns `Err`. ## remaining elements after that occurrence. If the delimiter is not found, returns `Err`.
## ##
## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Bar], after: [Baz] } ## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Bar], after: [Baz] }
splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound]* splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound]* | elem has Eq
splitLast = \list, delimiter -> splitLast = \list, delimiter ->
when List.findLastIndex list (\elem -> elem == delimiter) is when List.findLastIndex list (\elem -> elem == delimiter) is
Ok index -> Ok index ->

View file

@ -146,6 +146,7 @@ interface Num
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
Result.{ Result }, Result.{ Result },
Eq,
] ]
## Represents a number that could be either an [Int] or a [Frac]. ## Represents a number that could be either an [Int] or a [Frac].
@ -793,7 +794,7 @@ div : Frac a, Frac a -> Frac a
divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]* divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]*
divChecked = \a, b -> divChecked = \a, b ->
if b == 0 then if Num.isZero b then
Err DivByZero Err DivByZero
else else
Ok (Num.div a b) Ok (Num.div a b)
@ -802,7 +803,7 @@ divCeil : Int a, Int a -> Int a
divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]* divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]*
divCeilChecked = \a, b -> divCeilChecked = \a, b ->
if b == 0 then if Num.isZero b then
Err DivByZero Err DivByZero
else else
Ok (Num.divCeil a b) Ok (Num.divCeil a b)
@ -827,7 +828,7 @@ divTrunc : Int a, Int a -> Int a
divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]* divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]*
divTruncChecked = \a, b -> divTruncChecked = \a, b ->
if b == 0 then if Num.isZero b then
Err DivByZero Err DivByZero
else else
Ok (Num.divTrunc a b) Ok (Num.divTrunc a b)
@ -847,7 +848,7 @@ rem : Int a, Int a -> Int a
remChecked : Int a, Int a -> Result (Int a) [DivByZero]* remChecked : Int a, Int a -> Result (Int a) [DivByZero]*
remChecked = \a, b -> remChecked = \a, b ->
if b == 0 then if Num.isZero b then
Err DivByZero Err DivByZero
else else
Ok (Num.rem a b) Ok (Num.rem a b)

View file

@ -14,9 +14,11 @@ interface Set
intersection, intersection,
difference, difference,
] ]
imports [List, Bool.{ Bool }, Dict.{ Dict }, Num.{ Nat }] imports [List, Bool.{ Bool }, Eq.{ Eq }, Dict.{ Dict }, Num.{ Nat }]
Set k := Dict.Dict k {} Set k := Dict.Dict k {} has [Eq { isEq: setEq }]
setEq = \@Set d1, @Set d2 -> d1 == d2
fromDict : Dict k {} -> Set k fromDict : Dict k {} -> Set k
fromDict = \dict -> @Set dict fromDict = \dict -> @Set dict
@ -35,7 +37,7 @@ 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 | k has Eq
insert = \@Set dict, key -> insert = \@Set dict, key ->
dict dict
|> Dict.insert key {} |> Dict.insert key {}
@ -75,11 +77,11 @@ expect
actual == 3 actual == 3
## 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 | k has Eq
remove = \@Set dict, key -> remove = \@Set dict, key ->
@Set (Dict.remove dict key) @Set (Dict.remove dict key)
contains : Set k, k -> Bool contains : Set k, k -> Bool | k has Eq
contains = \set, key -> contains = \set, key ->
set set
|> Set.toDict |> Set.toDict
@ -89,21 +91,21 @@ toList : Set k -> List k
toList = \@Set dict -> toList = \@Set dict ->
Dict.keys dict Dict.keys dict
fromList : List k -> Set k fromList : List k -> Set k | k has Eq
fromList = \list -> fromList = \list ->
initial = @Set (Dict.withCapacity (List.len list)) initial = @Set (Dict.withCapacity (List.len list))
List.walk list initial \set, key -> Set.insert set key List.walk list initial \set, key -> Set.insert set key
union : Set k, Set k -> Set k union : Set k, Set k -> Set k | k has Eq
union = \@Set dict1, @Set dict2 -> union = \@Set dict1, @Set dict2 ->
@Set (Dict.insertAll dict1 dict2) @Set (Dict.insertAll dict1 dict2)
intersection : Set k, Set k -> Set k intersection : Set k, Set k -> Set k | k has Eq
intersection = \@Set dict1, @Set dict2 -> intersection = \@Set dict1, @Set dict2 ->
@Set (Dict.keepShared dict1 dict2) @Set (Dict.keepShared dict1 dict2)
difference : Set k, Set k -> Set k difference : Set k, Set k -> Set k | k has Eq
difference = \@Set dict1, @Set dict2 -> difference = \@Set dict1, @Set dict2 ->
@Set (Dict.removeAll dict1 dict2) @Set (Dict.removeAll dict1 dict2)

View file

@ -48,6 +48,7 @@ interface Str
] ]
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
Eq.{ Eq },
Result.{ Result }, Result.{ Result },
List, List,
Num.{ Nat, Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec }, Num.{ Nat, Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec },

View file

@ -409,7 +409,7 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
Percent => (ModuleName::NUM, "rem"), Percent => (ModuleName::NUM, "rem"),
Plus => (ModuleName::NUM, "add"), Plus => (ModuleName::NUM, "add"),
Minus => (ModuleName::NUM, "sub"), Minus => (ModuleName::NUM, "sub"),
Equals => (ModuleName::BOOL, "isEq"), Equals => (ModuleName::EQ, "isEq"),
NotEquals => (ModuleName::BOOL, "isNotEq"), NotEquals => (ModuleName::BOOL, "isNotEq"),
LessThan => (ModuleName::NUM, "isLt"), LessThan => (ModuleName::NUM, "isLt"),
GreaterThan => (ModuleName::NUM, "isGt"), GreaterThan => (ModuleName::NUM, "isGt"),