Prefer and and or for boolean operators

This commit is contained in:
Sam Mohr 2025-01-17 16:11:20 -08:00
parent d9d3fc74fc
commit a292e070d4
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
35 changed files with 189 additions and 282 deletions

View file

@ -1,4 +1,4 @@
module [Bool, Eq, true, false, and, or, not, is_eq, is_not_eq]
module [Bool, Eq, true, false, not, is_eq, is_not_eq]
## Defines a type that can be compared for total equality.
##
@ -44,55 +44,6 @@ true = @Bool(True)
false : Bool
false = @Bool(False)
## Returns `Bool.true` when both inputs are `Bool.true`. This is equivalent to
## the logic [AND](https://en.wikipedia.org/wiki/Logical_conjunction)
## gate. The infix operator `&&` can also be used as shorthand for
## `Bool.and`.
##
## ```roc
## expect Bool.and(Bool.true, Bool.true) == Bool.true
## expect (Bool.true && Bool.true) == Bool.true
## expect (Bool.false && Bool.true) == Bool.false
## expect (Bool.true && Bool.false) == Bool.false
## expect (Bool.false && Bool.false) == Bool.false
## ```
##
## ## Performance Details
##
## In Roc the `&&` and `||` work the same way as any
## other function. However, in some languages `&&` and `||` are special-cased.
## In these languages the compiler will skip evaluating the expression after the
## first operator under certain circumstances. For example an expression like
## `enable_pets && likes_dogs(user)` would compile to.
## ```roc
## if enable_pets then
## likes_dogs(user)
## else
## Bool.false
## ```
## Roc does not do this because conditionals like `if` and `when` have a
## performance cost. Calling a function can sometimes be faster across the board
## than doing an `if` to decide whether to skip calling it.
and : Bool, Bool -> Bool
## Returns `Bool.true` when either input is a `Bool.true`. This is equivalent to
## the logic [OR](https://en.wikipedia.org/wiki/Logical_disjunction) gate.
## The infix operator `||` can also be used as shorthand for `Bool.or`.
## ```roc
## expect Bool.or(Bool.false, Bool.true) == Bool.true
## expect (Bool.true || Bool.true) == Bool.true
## expect (Bool.false || Bool.true) == Bool.true
## expect (Bool.true || Bool.false) == Bool.true
## expect (Bool.false || Bool.false) == Bool.false
## ```
##
## ## Performance Details
##
## In Roc the `&&` and `||` work the same way as any
## other functions. However, in some languages `&&` and `||` are special-cased.
## Refer to the note in `Bool.and` for more detail.
or : Bool, Bool -> Bool
## Returns `Bool.false` when given `Bool.true`, and vice versa. This is
## equivalent to the logic [NOT](https://en.wikipedia.org/wiki/Negation)
## gate. The operator `!` can also be used as shorthand for `Bool.not`.

View file

@ -170,7 +170,7 @@ reserve = |@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capac
size = Num.min(requested_size, max_size)
requested_shifts = calc_shifts_for_size(size, max_load_factor)
if List.is_empty(buckets) || requested_shifts > shifts then
if List.is_empty(buckets) or requested_shifts > shifts then
(buckets0, max_bucket_capacity) = alloc_buckets_from_shift(requested_shifts, max_load_factor)
buckets1 = fill_buckets_from_data(buckets0, data, requested_shifts)
@Dict(
@ -915,7 +915,7 @@ calc_shifts_for_size_helper = |shifts, size, max_load_factor|
|> Num.to_f32
|> Num.mul(max_load_factor)
|> Num.floor
if shifts > 0 && max_bucket_capacity < size then
if shifts > 0 and max_bucket_capacity < size then
calc_shifts_for_size_helper(Num.sub_wrap(shifts, 1), size, max_load_factor)
else
shifts
@ -1087,7 +1087,7 @@ expect
|> insert("bar", {})
|> insert("baz", {})
contains(dict, "baz") && !(contains(dict, "other"))
contains(dict, "baz") and !(contains(dict, "other"))
expect
dict =
@ -1145,17 +1145,17 @@ expect
|> insert("l", 11)
(get(dict, "a") == Ok(0))
&& (get(dict, "b") == Ok(1))
&& (get(dict, "c") == Ok(2))
&& (get(dict, "d") == Ok(3))
&& (get(dict, "e") == Ok(4))
&& (get(dict, "f") == Ok(5))
&& (get(dict, "g") == Ok(6))
&& (get(dict, "h") == Ok(7))
&& (get(dict, "i") == Ok(8))
&& (get(dict, "j") == Ok(9))
&& (get(dict, "k") == Ok(10))
&& (get(dict, "l") == Ok(11))
and (get(dict, "b") == Ok(1))
and (get(dict, "c") == Ok(2))
and (get(dict, "d") == Ok(3))
and (get(dict, "e") == Ok(4))
and (get(dict, "f") == Ok(5))
and (get(dict, "g") == Ok(6))
and (get(dict, "h") == Ok(7))
and (get(dict, "i") == Ok(8))
and (get(dict, "j") == Ok(9))
and (get(dict, "k") == Ok(10))
and (get(dict, "l") == Ok(11))
# Force rehash.
expect
@ -1197,18 +1197,18 @@ expect
|> insert("m", 12)
(get(dict, "a") == Ok(0))
&& (get(dict, "b") == Ok(1))
&& (get(dict, "c") == Ok(2))
&& (get(dict, "d") == Ok(3))
&& (get(dict, "e") == Ok(4))
&& (get(dict, "f") == Ok(5))
&& (get(dict, "g") == Ok(6))
&& (get(dict, "h") == Ok(7))
&& (get(dict, "i") == Ok(8))
&& (get(dict, "j") == Ok(9))
&& (get(dict, "k") == Ok(10))
&& (get(dict, "l") == Ok(11))
&& (get(dict, "m") == Ok(12))
and (get(dict, "b") == Ok(1))
and (get(dict, "c") == Ok(2))
and (get(dict, "d") == Ok(3))
and (get(dict, "e") == Ok(4))
and (get(dict, "f") == Ok(5))
and (get(dict, "g") == Ok(6))
and (get(dict, "h") == Ok(7))
and (get(dict, "i") == Ok(8))
and (get(dict, "j") == Ok(9))
and (get(dict, "k") == Ok(10))
and (get(dict, "l") == Ok(11))
and (get(dict, "m") == Ok(12))
expect
empty({})
@ -1266,7 +1266,7 @@ expect
bad_keys,
Bool.true,
|acc, k|
acc && Dict.contains(dict, k),
acc and Dict.contains(dict, k),
)
all_inserted_correctly

View file

@ -1361,7 +1361,7 @@ split_last = |list, delimiter|
## result is an empty list.
chunks_of : List a, U64 -> List (List a)
chunks_of = |list, chunk_size|
if chunk_size == 0 || List.is_empty(list) then
if chunk_size == 0 or List.is_empty(list) then
[]
else
chunk_capacity = Num.div_ceil(List.len(list), chunk_size)

View file

@ -619,9 +619,9 @@ is_gte : Num a, Num a -> Bool
## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).)
is_approx_eq : Frac a, Frac a, { rtol ?? Frac a, atol ?? Frac a } -> Bool
is_approx_eq = |x, y, { rtol ?? 0.00001, atol ?? 0.00000001 }|
eq = x <= y && x >= y
eq = x <= y and x >= y
meets_tolerance = Num.abs_diff(x, y) <= Num.max(atol, (rtol * Num.max(Num.abs(x), Num.abs(y))))
eq || meets_tolerance
eq or meets_tolerance
## Returns `Bool.true` if the number is `0`, and `Bool.false` otherwise.
is_zero : Num a -> Bool

View file

@ -972,7 +972,7 @@ matches_at_help = |state|
},
)
does_this_match && does_rest_match
does_this_match and does_rest_match
## Walks over the `UTF-8` bytes of the given [Str] and calls a function to update
## state for each byte. The index for that byte in the string is provided

View file

@ -212,8 +212,6 @@ map_symbol_to_lowlevel_and_arity! {
Eq; BOOL_STRUCTURAL_EQ; 2,
NotEq; BOOL_STRUCTURAL_NOT_EQ; 2,
And; BOOL_AND; 2,
Or; BOOL_OR; 2,
Not; BOOL_NOT; 1,
BoxExpr; BOX_BOX_FUNCTION; 1,
UnboxExpr; BOX_UNBOX; 1,

View file

@ -4,7 +4,6 @@ use crate::env::Env;
use crate::scope::Scope;
use bumpalo::collections::Vec;
use roc_error_macros::internal_error;
use roc_module::called_via::BinOp::{DoubleQuestion, Pizza, SingleQuestion};
use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *};
@ -36,7 +35,7 @@ fn new_op_call_expr<'a>(
let value = match loc_op.value {
// Rewrite the Pizza operator into an Apply
Pizza => {
BinOp::Pizza => {
// Allow `left |> try (optional)` to desugar to `try left (optional)`
let right_without_spaces = without_spaces(&right.value);
match right_without_spaces {
@ -190,7 +189,7 @@ fn new_op_call_expr<'a>(
let args = args.into_bump_slice();
Apply(function, args, CalledVia::BinOp(Pizza))
Apply(function, args, CalledVia::BinOp(BinOp::Pizza))
}
PncApply(function, arguments) => {
let mut args = Vec::with_capacity_in(1 + arguments.len(), env.arena);
@ -200,16 +199,20 @@ fn new_op_call_expr<'a>(
let args = args.into_bump_slice();
Apply(function, args, CalledVia::BinOp(Pizza))
Apply(function, args, CalledVia::BinOp(BinOp::Pizza))
}
Dbg => *desugar_dbg_expr(env, scope, left, region),
_ => {
// e.g. `1 |> (if b then (\a -> a) else (\c -> c))`
Apply(right, env.arena.alloc([left]), CalledVia::BinOp(Pizza))
Apply(
right,
env.arena.alloc([left]),
CalledVia::BinOp(BinOp::Pizza),
)
}
}
}
DoubleQuestion => {
BinOp::DoubleQuestion => {
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
@ -259,7 +262,7 @@ fn new_op_call_expr<'a>(
When(left, &*env.arena.alloc([ok_branch, err_branch]))
}
SingleQuestion => {
BinOp::SingleQuestion => {
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
@ -340,6 +343,41 @@ fn new_op_call_expr<'a>(
When(left, &*env.arena.alloc([ok_branch, err_branch]))
}
BinOp::And => {
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
Expr::If {
if_thens: env.arena.alloc([(*left, *right)]),
final_else: env.arena.alloc(Loc::at(
right.region,
Expr::Var {
module_name: ModuleName::BOOL,
ident: "false",
},
)),
indented_else: false,
}
}
BinOp::Or => {
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
Expr::If {
if_thens: env.arena.alloc([(
*left,
Loc::at(
right.region,
Expr::Var {
module_name: ModuleName::BOOL,
ident: "true",
},
),
)]),
final_else: right,
indented_else: false,
}
}
binop => {
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
@ -1641,8 +1679,8 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
GreaterThan => (ModuleName::NUM, "is_gt"),
LessThanOrEq => (ModuleName::NUM, "is_lte"),
GreaterThanOrEq => (ModuleName::NUM, "is_gte"),
And => (ModuleName::BOOL, "and"),
Or => (ModuleName::BOOL, "or"),
And => unreachable!("Cannot desugar the `and` operator"),
Or => unreachable!("Cannot desugar the `or` operator"),
Pizza => unreachable!("Cannot desugar the |> operator"),
DoubleQuestion => unreachable!("Cannot desugar the ?? operator"),
SingleQuestion => unreachable!("Cannot desugar the ? operator"),

View file

@ -959,8 +959,8 @@ fn push_op(buf: &mut Buf, op: BinOp) {
called_via::BinOp::GreaterThan => buf.push('>'),
called_via::BinOp::LessThanOrEq => buf.push_str("<="),
called_via::BinOp::GreaterThanOrEq => buf.push_str(">="),
called_via::BinOp::And => buf.push_str("&&"),
called_via::BinOp::Or => buf.push_str("||"),
called_via::BinOp::Or => buf.push_str("or"),
called_via::BinOp::And => buf.push_str("and"),
called_via::BinOp::Pizza => buf.push_str("|>"),
called_via::BinOp::DoubleQuestion => buf.push_str("??"),
called_via::BinOp::SingleQuestion => buf.push_str("?"),

View file

@ -1268,20 +1268,6 @@ trait Backend<'a> {
internal_error!("bitwise xor on a non-integer")
}
}
LowLevel::And => {
if let LayoutRepr::Builtin(Builtin::Bool) = self.interner().get_repr(*ret_layout) {
self.build_int_bitwise_and(sym, &args[0], &args[1], IntWidth::U8)
} else {
internal_error!("bitwise and on a non-integer")
}
}
LowLevel::Or => {
if let LayoutRepr::Builtin(Builtin::Bool) = self.interner().get_repr(*ret_layout) {
self.build_int_bitwise_or(sym, &args[0], &args[1], IntWidth::U8)
} else {
internal_error!("bitwise or on a non-integer")
}
}
LowLevel::NumShiftLeftBy => {
if let LayoutRepr::Builtin(Builtin::Int(int_width)) =
self.interner().get_repr(*ret_layout)

View file

@ -1284,30 +1284,6 @@ pub(crate) fn run_low_level<'a, 'ctx>(
rhs_layout,
)
}
And => {
// The (&&) operator
arguments!(lhs_arg, rhs_arg);
let bool_val = env.builder.new_build_and(
lhs_arg.into_int_value(),
rhs_arg.into_int_value(),
"bool_and",
);
BasicValueEnum::IntValue(bool_val)
}
Or => {
// The (||) operator
arguments!(lhs_arg, rhs_arg);
let bool_val = env.builder.new_build_or(
lhs_arg.into_int_value(),
rhs_arg.into_int_value(),
"bool_or",
);
BasicValueEnum::IntValue(bool_val)
}
Not => {
// The (!) operator
arguments!(arg);

View file

@ -2163,14 +2163,6 @@ impl<'a> LowLevelCall<'a> {
NumF64ToParts => self.load_args_and_call_zig(backend, bitcode::NUM_F64_TO_PARTS),
NumF32FromParts => self.load_args_and_call_zig(backend, bitcode::NUM_F32_FROM_PARTS),
NumF64FromParts => self.load_args_and_call_zig(backend, bitcode::NUM_F64_FROM_PARTS),
And => {
self.load_args(backend);
backend.code_builder.i32_and();
}
Or => {
self.load_args(backend);
backend.code_builder.i32_or();
}
Not => {
self.load_args(backend);
backend.code_builder.i32_eqz();

View file

@ -6036,7 +6036,7 @@ All branches in an `if` must have the same type!
double_binop,
indoc!(
r"
key >= 97 && <= 122
key >= 97 and <= 122
"
),
@r"

View file

@ -62,8 +62,8 @@ const DISPLAY_STRINGS: [(BinOp, &str); 18] = [
(GreaterThan, ">"),
(LessThanOrEq, "<="),
(GreaterThanOrEq, ">="),
(And, "&&"),
(Or, "||"),
(And, "and"),
(Or, "or"),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -199,7 +199,7 @@ pub enum Associativity {
/// right-associative operators:
///
/// exponentiation: ^
/// boolean: && ||
/// boolean: and or
/// application: <|
RightAssociative,

View file

@ -109,8 +109,6 @@ pub enum LowLevel {
NumF64FromParts,
Eq,
NotEq,
And,
Or,
Not,
Hash,
PtrCast,
@ -343,8 +341,6 @@ map_symbol_to_lowlevel! {
NumF64FromParts <= NUM_F64_FROM_PARTS;
Eq <= BOOL_STRUCTURAL_EQ;
NotEq <= BOOL_STRUCTURAL_NOT_EQ;
And <= BOOL_AND;
Or <= BOOL_OR;
Not <= BOOL_NOT;
Unreachable <= LIST_UNREACHABLE;
DictPseudoSeed <= DICT_PSEUDO_SEED;

View file

@ -1355,16 +1355,14 @@ define_builtins! {
0 BOOL_BOOL: "Bool" exposed_type=true // the Bool.Bool type alias
1 BOOL_FALSE: "false"
2 BOOL_TRUE: "true"
3 BOOL_AND: "and"
4 BOOL_OR: "or"
5 BOOL_NOT: "not"
6 BOOL_XOR: "xor"
7 BOOL_NEQ: "is_not_eq"
8 BOOL_EQ: "Eq" exposed_type=true
9 BOOL_IS_EQ: "is_eq"
10 BOOL_IS_EQ_IMPL: "bool_is_eq"
unexposed 11 BOOL_STRUCTURAL_EQ: "structural_eq"
unexposed 12 BOOL_STRUCTURAL_NOT_EQ: "structural_not_eq"
3 BOOL_NOT: "not"
4 BOOL_XOR: "xor"
5 BOOL_NEQ: "is_not_eq"
6 BOOL_EQ: "Eq" exposed_type=true
7 BOOL_IS_EQ: "is_eq"
8 BOOL_IS_EQ_IMPL: "bool_is_eq"
unexposed 9 BOOL_STRUCTURAL_EQ: "structural_eq"
unexposed 10 BOOL_STRUCTURAL_NOT_EQ: "structural_not_eq"
}
5 STR: "Str" => {
0 STR_STR: "Str" exposed_apply_type=true // the Str.Str type alias

View file

@ -1562,7 +1562,7 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
Eq | NotEq => RC::NoRc,
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivFrac
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf

View file

@ -1261,7 +1261,7 @@ pub(crate) fn lowlevel_borrow_signature(op: LowLevel) -> &'static [Ownership] {
Eq | NotEq => &[BORROWED, BORROWED],
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivFrac
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf

View file

@ -738,7 +738,7 @@ fn parse_stmt_operator_chain<'a>(
| Expr::Apply(
Loc {
region: _,
value: Expr::Tag(..)
value: Expr::Tag(_)
},
&[],
_
@ -752,7 +752,7 @@ fn parse_stmt_operator_chain<'a>(
// try an operator
return parse_stmt_after_apply(
arena,
state.clone(),
state,
min_indent,
call_min_indent,
expr_state,
@ -1934,43 +1934,6 @@ fn parse_stmt_after_apply<'a>(
}
}
// #[allow(clippy::too_many_arguments)]
// fn parse_expr_after_apply<'a>(
// arena: &'a Bump,
// state: State<'a>,
// min_indent: u32,
// call_min_indent: u32,
// check_for_arrow: CheckForArrow,
// check_for_defs: bool,
// mut expr_state: ExprState<'a>,
// before_op: State<'a>,
// initial_state: State<'a>,
// ) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> {
// match loc(bin_op(check_for_defs)).parse(arena, state.clone(), call_min_indent) {
// Err((MadeProgress, f)) => Err((MadeProgress, f)),
// Ok((_, loc_op, state)) => {
// expr_state.consume_spaces(arena);
// let initial_state = before_op;
// parse_expr_operator(
// arena,
// state,
// min_indent,
// call_min_indent,
// options,
// check_for_defs,
// expr_state,
// loc_op,
// initial_state,
// )
// }
// Err((NoProgress, _)) => {
// let expr = parse_expr_final(expr_state, arena);
// // roll back space parsing
// Ok((MadeProgress, expr, initial_state))
// }
// }
// }
#[allow(clippy::too_many_arguments)]
fn parse_apply_arg<'a>(
arena: &'a Bump,
@ -4069,6 +4032,24 @@ where
G: Fn(&'a str, Position) -> E,
E: 'a,
{
match state.bytes() {
&[b'o', b'r', ..] => {
return Ok((
MadeProgress,
OperatorOrDef::BinOp(BinOp::Or),
state.advance(2),
))
}
&[b'a', b'n', b'd', ..] => {
return Ok((
MadeProgress,
OperatorOrDef::BinOp(BinOp::And),
state.advance(3),
))
}
_ => {}
}
let chomped = chomp_ops(state.bytes());
macro_rules! good {

View file

@ -10,6 +10,8 @@ pub const IMPORT: &str = "import";
pub const EXPECT: &str = "expect";
pub const RETURN: &str = "return";
pub const CRASH: &str = "crash";
pub const AND: &str = "and";
pub const OR: &str = "or";
// These keywords are valid in imports
pub const EXPOSING: &str = "exposing";
@ -21,8 +23,8 @@ pub const WHERE: &str = "where";
// These keywords are valid in headers
pub const PLATFORM: &str = "platform";
pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, RETURN, CRASH,
pub const KEYWORDS: [&str; 13] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, RETURN, CRASH, AND, OR,
];
pub fn is_allowed_identifier(mut ident: &str) -> bool {

View file

@ -2095,13 +2095,13 @@ mod eq {
LyingEq := U8 implements [Eq {is_eq}]
is_eq = \@LyingEq m, @LyingEq n -> m != n
is_eq = \@LyingEq(m), @LyingEq(n) -> m != n
main =
a = @LyingEq 10
b = @LyingEq 5
c = @LyingEq 5
if Bool.is_eq a b && !(Bool.is_eq b c) then
a = @LyingEq(10)
b = @LyingEq(5)
c = @LyingEq(5)
if Bool.is_eq(a, b) and !(Bool.is_eq(b, c)) then
"okay"
else
"fail"

View file

@ -121,7 +121,7 @@ fn bool_logic() {
bool2 = Bool.false
bool3 = !bool1
(bool1 && bool2) || bool2 && bool3
(bool1 and bool2) or bool2 and bool3
"#
),
false,
@ -132,19 +132,19 @@ fn bool_logic() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn and_bool() {
assert_evals_to!("Bool.true && Bool.true", true, bool);
assert_evals_to!("Bool.true && Bool.false", false, bool);
assert_evals_to!("Bool.false && Bool.true", false, bool);
assert_evals_to!("Bool.false && Bool.false", false, bool);
assert_evals_to!("Bool.true and Bool.true", true, bool);
assert_evals_to!("Bool.true and Bool.false", false, bool);
assert_evals_to!("Bool.false and Bool.true", false, bool);
assert_evals_to!("Bool.false and Bool.false", false, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn or_bool() {
assert_evals_to!("Bool.true || Bool.true", true, bool);
assert_evals_to!("Bool.true || Bool.false", true, bool);
assert_evals_to!("Bool.false || Bool.true", true, bool);
assert_evals_to!("Bool.false || Bool.false", false, bool);
assert_evals_to!("Bool.true or Bool.true", true, bool);
assert_evals_to!("Bool.true or Bool.false", true, bool);
assert_evals_to!("Bool.false or Bool.true", true, bool);
assert_evals_to!("Bool.false or Bool.false", false, bool);
}
#[test]
@ -544,7 +544,7 @@ fn eq_different_rosetrees() {
cd = c2 == d2
ab && cd
ab and cd
"#
),
true,

View file

@ -158,19 +158,19 @@ fn even_odd() {
assert_evals_to!(
indoc!(
r"
even = \n ->
even = |n|
when n is
0 -> Bool.true
1 -> Bool.false
_ -> odd (n - 1)
_ -> odd(n - 1)
odd = \n ->
odd = |n|
when n is
0 -> Bool.false
1 -> Bool.true
_ -> even (n - 1)
_ -> even(n - 1)
odd 5 && even 42
odd(5) and even(42)
"
),
true,
@ -2315,12 +2315,12 @@ fn recursive_tag_id_in_allocation_eq() {
]
x : Value
x = G 42
x = G(42)
y : Value
y = H 42
y = H(42)
main = (x == x) && (x != y) && (y == y)
main = x == x and x != y and y == y
"#
),
true,

View file

@ -2460,7 +2460,7 @@ fn issue_4557() {
app "test" provides [main] to "./platform"
is_eq_q = \q1, q2 -> when T q1 q2 is
T (U f1) (U f2) -> Bool.or (is_eq_q (U f2) (U f1)) (f1 {} == f2 {})
T (U f1) (U f2) -> (is_eq_q (U f2) (U f1)) or (f1 {} == f2 {})
main = is_eq_q (U \{} -> "a") (U \{} -> "a")
"#

View file

@ -2,11 +2,11 @@ app "test" provides [main] to "./platform"
main =
s1 : Set U8
s1 = Set.empty {}
s1 = Set.empty({})
s2 : Set Str
s2 = Set.empty {}
s2 = Set.empty({})
Bool.is_eq s1 s1 && Bool.is_eq s2 s2
# ^^^^^^^^^^ Set#Bool.is_eq(31): Set Str, Set Str -[[Set.is_eq(31)]]-> Bool
Bool.is_eq(s1, s1) and Bool.is_eq(s2, s2)
# ^^^^^^^^^^ Set#Bool.is_eq(31): Set Str, Set Str -[[Set.is_eq(31)]]-> Bool
# ^^^^^^^^^^ Set#Bool.is_eq(31): Set U8, Set U8 -[[Set.is_eq(31)]]-> Bool