Move Eq to Bool

This commit is contained in:
Ayaz Hafiz 2022-10-10 15:22:57 -05:00
parent bd5f5ed735
commit a256947a9f
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
19 changed files with 92 additions and 101 deletions

View file

@ -1,8 +1,34 @@
interface Bool
exposes [Bool, true, false, and, or, not, isNotEq]
exposes [Bool, Eq, true, false, and, or, not, isEq, isNotEq, structuralEq]
imports []
Bool := [True, False]
## A type that can be compared for total equality.
##
## Total equality means that all values of the type can be compared to each
## other, and two values `a`, `b` are identical if and only if `isEq a b` is
## `Bool.true`.
##
## Not all types support total equality. For example, an [F32] or [F64] can
## be a `NaN` ([not a number](https://en.wikipedia.org/wiki/NaN)), and the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754)
## floating point standard specifies that two `NaN`s are never equal to each other.
Eq has
## Returns `Bool.true` if the two values are equal, and `Bool.false` otherwise.
##
## `a == b` is shorthand for `Eq.isEq a b`.
##
## When `isEq` is derived by the Roc compiler, values are compared via
## structural equality. 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*.
## 5. Functions can never be compared for structural equality. Roc cannot derive `isEq` for types that contain functions!
isEq : a, a -> Bool | a has Eq
Bool := [True, False] has [Eq {isEq: boolIsEq}]
boolIsEq = \@Bool b1, @Bool b2 -> structuralEq b1 b2
## The boolean true value.
true : Bool
@ -74,3 +100,13 @@ not : Bool -> Bool
## Note that `isNotEq` takes `'val` instead of `val`, which means `isNotEq` does not
## accept arguments whose types contain functions.
isNotEq : a, a -> Bool
# ## Calls [isEq] on the given values, then calls [not] on the result.
# ##
# ## `a != b` is shorthand for `Eq.isNotEq a b`.
# isNotEq : a, a -> Bool | a has Eq
# isNotEq = \a, b -> Bool.not (isEq a b)
# INTERNAL COMPILER USE ONLY: used to lower calls to `isEq` to structural
# equality via the `Eq` low-level for derived types.
structuralEq : a, a -> Bool

View file

@ -18,8 +18,7 @@ interface Dict
removeAll,
]
imports [
Bool.{ Bool },
Eq.{ Eq },
Bool.{ Bool, Eq },
Result.{ Result },
List,
Num.{ Nat },

View file

@ -1,44 +0,0 @@
interface Eq
exposes [
Eq,
isEq,
isNotEq,
structuralEq,
]
imports [
Bool.{ Bool },
]
## A type that can be compared for total equality.
##
## Total equality means that all values of the type can be compared to each
## other, and two values `a`, `b` are identical if and only if `isEq a b` is
## `Bool.true`.
##
## Not all types support total equality. For example, an [F32] or [F64] can
## be a `NaN` ([not a number](https://en.wikipedia.org/wiki/NaN)), and the [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754)
## floating point standard specifies that two `NaN`s are never equal to each other.
Eq has
## Returns `Bool.true` if the two values are equal, and `Bool.false` otherwise.
##
## `a == b` is shorthand for `Eq.isEq a b`.
##
## When `isEq` is derived by the Roc compiler, values are compared via
## structural equality. 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*.
## 5. Functions can never be compared for structural equality. Roc cannot derive `isEq` for types that contain functions!
isEq : a, a -> Bool | a has Eq
## Calls [isEq] on the given values, then calls [not] on the result.
##
## `a != b` is shorthand for `Eq.isNotEq a b`.
isNotEq : a, a -> Bool | a has Eq
isNotEq = \a, b -> Bool.not (isEq a b)
# INTERNAL COMPILER USE ONLY: used to lower calls to `isEq` to structural
# equality via the `Eq` low-level for derived types.
structuralEq : a, a -> Bool

View file

@ -33,8 +33,7 @@ interface Json
F64,
Dec,
},
Bool.{ Bool },
Eq.{ Eq },
Bool.{ Bool, Eq },
Result,
]

View file

@ -65,8 +65,7 @@ interface List
countIf,
]
imports [
Bool.{ Bool },
Eq.{ Eq },
Bool.{ Bool, Eq },
Result.{ Result },
Num.{ Nat, Num, Int },
]

View file

@ -146,7 +146,6 @@ interface Num
imports [
Bool.{ Bool },
Result.{ Result },
Eq,
]
## Represents a number that could be either an [Int] or a [Frac].

View file

@ -14,7 +14,7 @@ interface Set
intersection,
difference,
]
imports [List, Bool.{ Bool }, Eq.{ Eq }, Dict.{ Dict }, Num.{ Nat }]
imports [List, Bool.{ Bool, Eq }, Dict.{ Dict }, Num.{ Nat }]
Set k := Dict.Dict k {} has [Eq { isEq: setEq }]

View file

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

View file

@ -14,7 +14,6 @@ pub fn module_source(module_id: ModuleId) -> &'static str {
ModuleId::ENCODE => ENCODE,
ModuleId::DECODE => DECODE,
ModuleId::HASH => HASH,
ModuleId::EQ => EQ,
ModuleId::JSON => JSON,
_ => panic!(
"ModuleId {:?} is not part of the standard library",
@ -34,5 +33,4 @@ const BOOL: &str = include_str!("../roc/Bool.roc");
const ENCODE: &str = include_str!("../roc/Encode.roc");
const DECODE: &str = include_str!("../roc/Decode.roc");
const HASH: &str = include_str!("../roc/Hash.roc");
const EQ: &str = include_str!("../roc/Eq.roc");
const JSON: &str = include_str!("../roc/Json.roc");

View file

@ -192,7 +192,6 @@ map_symbol_to_lowlevel_and_arity! {
NumShiftRightZfBy; NUM_SHIFT_RIGHT_ZERO_FILL; 2,
NumToStr; NUM_TO_STR; 1,
Eq; BOOL_EQ; 2,
Eq; EQ_STRUCTURAL_EQ; 2,
NotEq; BOOL_NEQ; 2,
And; BOOL_AND; 2,

View file

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

View file

@ -178,7 +178,6 @@ impl Default for ModuleCache<'_> {
ENCODE,
DECODE,
HASH,
EQ,
JSON,
}
@ -2248,7 +2247,6 @@ fn update<'a>(
extend_header_with_builtin(&mut header, ModuleId::ENCODE);
extend_header_with_builtin(&mut header, ModuleId::DECODE);
extend_header_with_builtin(&mut header, ModuleId::HASH);
extend_header_with_builtin(&mut header, ModuleId::EQ);
}
state
@ -3283,7 +3281,6 @@ fn load_module<'a>(
"Encode", ModuleId::ENCODE
"Decode", ModuleId::DECODE
"Hash", ModuleId::HASH
"Eq", ModuleId::EQ
"Json", ModuleId::JSON
}
@ -4781,7 +4778,6 @@ fn canonicalize_and_constrain<'a>(
| ModuleId::DICT
| ModuleId::SET
| ModuleId::HASH
| ModuleId::EQ
);
if !name.is_builtin() || should_include_builtin {

View file

@ -86,7 +86,6 @@ impl ModuleName {
pub const ENCODE: &'static str = "Encode";
pub const DECODE: &'static str = "Decode";
pub const HASH: &'static str = "Hash";
pub const EQ: &'static str = "Eq";
pub const JSON: &'static str = "Json";
pub fn as_str(&self) -> &str {

View file

@ -184,7 +184,6 @@ macro_rules! map_symbol_to_lowlevel {
// Below, we explicitly handle some exceptions to the pattern where a lowlevel maps
// directly to a symbol. If you are unsure if your lowlevel is an exception, assume
// that it isn't and just see if that works.
#[allow(unreachable_patterns)] // TODO: remove after we replace `BOOL_EQ` with `EQ_EQ` wholly
match lowlevel {
$(
LowLevel::$lowlevel => Symbol::$symbol,
@ -310,7 +309,6 @@ map_symbol_to_lowlevel! {
NumShiftRightBy <= NUM_SHIFT_RIGHT,
NumShiftRightZfBy <= NUM_SHIFT_RIGHT_ZERO_FILL,
NumToStr <= NUM_TO_STR,
Eq <= BOOL_EQ,
Eq <= EQ_STRUCTURAL_EQ,
NotEq <= BOOL_NEQ,
And <= BOOL_AND,

View file

@ -1251,8 +1251,11 @@ define_builtins! {
4 BOOL_OR: "or"
5 BOOL_NOT: "not"
6 BOOL_XOR: "xor"
7 BOOL_EQ: "isEq"
8 BOOL_NEQ: "isNotEq"
7 BOOL_NEQ: "isNotEq"
8 EQ_EQ: "Eq" exposed_type=true
9 EQ_IS_EQ: "isEq"
10 EQ_STRUCTURAL_EQ: "structuralEq"
11 BOOL_IS_EQ_IMPL: "boolIsEq"
}
5 STR: "Str" => {
0 STR_STR: "Str" exposed_apply_type=true // the Str.Str type alias
@ -1525,15 +1528,9 @@ define_builtins! {
15 HASH_HASH_STR_BYTES: "hashStrBytes"
16 HASH_HASH_LIST: "hashList"
}
14 EQ: "Eq" => {
0 EQ_EQ: "Eq" exposed_type=true
1 EQ_IS_EQ: "isEq"
2 EQ_IS_NOT_EQ: "isNotEq"
3 EQ_STRUCTURAL_EQ: "structuralEq"
}
15 JSON: "Json" => {
14 JSON: "Json" => {
0 JSON_JSON: "Json"
}
num_modules: 16 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
num_modules: 15 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
}

View file

@ -378,6 +378,7 @@ mod solve_expr {
let known_specializations = abilities_store.iter_declared_implementations().filter_map(
|(impl_key, member_impl)| match member_impl {
MemberImpl::Impl(impl_symbol) => {
dbg!(impl_symbol);
let specialization = abilities_store.specialization_info(*impl_symbol).expect(
"declared implementations should be resolved conclusively after solving",
);
@ -7984,7 +7985,7 @@ mod solve_expr {
isEq = \@Trivial {}, @Trivial {} -> Bool.true
main = Eq.isEq (@Trivial {}) (@Trivial {})
main = Bool.isEq (@Trivial {}) (@Trivial {})
"#
),
"Bool",

View file

@ -13,26 +13,42 @@ use roc_derive_key::DeriveBuiltin::IsEq;
#[test]
fn immediates() {
// Everything is an immediate for `Eq`.
check_single_lset_immediate(IsEq, v!(U8), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(U16), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(U32), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(U64), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(U128), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(I8), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(I16), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(I32), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(I64), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(I128), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(STR), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(Symbol::LIST_LIST v!(U8)), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(Symbol::LIST_LIST v!(STR)), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!({ a: v!(U8), }), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(EMPTY_RECORD), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!([ A v!(U8) v!(STR), B v!(STR) ]), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!([ A v!(U8) v!(STR), B v!(STR) ]), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!([ Nil, Cons v!(^lst)] as lst), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(U8), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U16), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U32), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U64), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(U128), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I8), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I16), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I32), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I64), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(I128), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(STR), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(Symbol::LIST_LIST v!(U8)), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(
IsEq,
v!(Symbol::LIST_LIST v!(STR)),
Symbol::EQ_STRUCTURAL_EQ,
);
check_single_lset_immediate(IsEq, v!({ a: v!(U8), }), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(IsEq, v!(EMPTY_RECORD), Symbol::EQ_STRUCTURAL_EQ);
check_single_lset_immediate(
IsEq,
v!([ A v!(U8) v!(STR), B v!(STR) ]),
Symbol::EQ_STRUCTURAL_EQ,
);
check_single_lset_immediate(
IsEq,
v!([ A v!(U8) v!(STR), B v!(STR) ]),
Symbol::EQ_STRUCTURAL_EQ,
);
check_single_lset_immediate(
IsEq,
v!([ Nil, Cons v!(^lst)] as lst),
Symbol::EQ_STRUCTURAL_EQ,
);
// NOTE: despite this reaching an immediate, `F64`s will never actually be allowed to be
// compared, because obligation checking will rule them out from `isEq`!
check_single_lset_immediate(IsEq, v!(F64), Symbol::BOOL_EQ);
check_single_lset_immediate(IsEq, v!(F64), Symbol::EQ_STRUCTURAL_EQ);
}

View file

@ -56,8 +56,8 @@ fn module_source_and_path(builtin: DeriveBuiltin) -> (ModuleId, &'static str, Pa
builtins_path.join("Hash.roc"),
),
DeriveBuiltin::IsEq => (
ModuleId::EQ,
module_source(ModuleId::EQ),
ModuleId::BOOL,
module_source(ModuleId::BOOL),
builtins_path.join("Eq.roc"),
),
}

View file

@ -1629,7 +1629,7 @@ mod eq {
a = @LyingEq 10
b = @LyingEq 5
c = @LyingEq 5
if Eq.isEq a b && !(Eq.isEq b c) then
if Bool.isEq a b && !(Bool.isEq b c) then
"okay"
else
"fail"
@ -1647,7 +1647,7 @@ mod eq {
r#"
app "test" provides [main] to "./platform"
main = Eq.isEq 10u8 10u8
main = Bool.isEq 10u8 10u8
"#
),
true,