Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@paulo-ferraz-oliveira
Copy link
Collaborator

Description

This is for the no_boolean_in_comparison bug. I think we should also be comparing with /= and =/=, so I add those.

Closes #441.

@elbrujohalcon elbrujohalcon added this to the 4.1.0 milestone Jun 9, 2025
@elbrujohalcon elbrujohalcon merged commit 6269a55 into main Jun 9, 2025
7 checks passed
@elbrujohalcon elbrujohalcon deleted the fix/no-boolean-in-comparison branch June 9, 2025 11:04
@filmor
Copy link
Contributor

filmor commented Jun 16, 2025

I think this one is a step back. When I write Val =:= true, I use this as a shorthand for is_boolean(Val) andalso Val. In general, IMO a =:= and =/= implies a type check, as that is what distinguishes it from == and /=.

@elbrujohalcon
Copy link
Member

@filmor Let me see if I understand you point. You're dealing with functions like this one?

-spec your_function(boolean() | map()) -> is_true | is_something_else.
your_function(Val) when Val =:= true -> is_true;
your_funciton(_) -> is_something_else.

(i.e., a function that does something if it receives true and something else in any other case, even if what it receives is not even a boolean)

If that's the case, I would just write…

-spec your_function(boolean() | map()) -> is_true | is_something_else.
your_function(true) -> is_true;
your_funciton(_) -> is_something_else.

Am I missing something here?

@filmor
Copy link
Contributor

filmor commented Jun 16, 2025

This is an actual diff I have now:

 -spec is_active() -> boolean().
 is_active() ->
     try
-        etp_config:get_value([?SECTION, <<"active">>]) =:= true
+        case etp_config:get_value([?SECTION, <<"active">>]) of
+            true ->
+                true;
+            _ ->
+                false
+        end
     catch
         _:_ ->
             true
     end.

The data comes out of a configuration file.

I'm not saying that this is a huge issue, and I'm aware that there are workarounds. What I try to get across is that =:= and =/= are semantically not simply comparisons but also type checks.

@elbrujohalcon
Copy link
Member

Hummm… I disagree on the type-checking nature of =:=. I don't think the community as a whole sees it that way.

In any case, I would write that code like…

 -spec is_active() -> boolean().
 is_active() ->
     try etp_config:get_value([?SECTION, <<"active">>]) of
        true -> true;
        _ -> false
     catch
         _:_ ->
             true %% BTW, shouldn't this be 'false' ? (o_O)
     end.

Yeah, it is larger than the original… but it also moves =:= true outside the try part and with that you can be sure that the only possible error/exception (if any) will come from etp_config:get_value/1. Yeah, I'm aware that =:= can't fail, but… in your original code it was part of the try anyway.

In any case, what changed now is the addition of =:= to the rule. Your code would've raised the warning anyway if you would've written etp_config:get_value([?SECTION, <<"active">>]) == true, which is (semantically) the same thing since there is no type-casting for booleans. Or is that what I'm missing here? Does Erlang do some magic type-casting for booleans? Is 0 == true? I think not… right?

As far as I know, == matches everything exactly like =:= except for numbers. But maybe I'm wrong.

@elbrujohalcon
Copy link
Member

Well… The docs are not super-explicit about it, but I think it's reasonable to infer that == is different than =:= only for numbers…

The term equivalence operators, =:= and =/=, return whether two terms are indistinguishable. While the other operators consider the same numbers equal even when their types differ (1 == 1.0 is true), the term equivalence operators return whether or not there exists a way to tell the arguments apart.

For example, while the terms 0 and 0.0 represent the same number, we can tell them apart by using the is_integer/1 function. Hence, =:= and =/= consider them different.

Furthermore, the terms 0.0 and -0.0 also represent the same number, but they yield different results when converted to string form through float_to_list/1: when given the former it returns a string without a sign, and when given the latter it returns a string with a sign. Therefore, =:= and =/= consider them different.

The term equivalence operators are useful when reasoning about terms as opaque values, for example in associative containers or memoized functions where using the equal-to operator (==) risks producing incorrect results as a consequence of mixing up numbers of different types.

@kivra-pauoli
Copy link
Contributor

We should improve our documentation, for sure. I also ran into this and believe that doing is_boolean(Val) andalso Val (we were doing fun(V) -> V =:= true end adds unwarranted implicitness, meaning you have to reason on why that is (on the other hand if you make your code explicit you work toward one of the rules' goals - less implicitness). There's maybe a case to have a list of true, false where we're looking at the value of the atom, and not its truthiness, but that's an edge case, I believe, since that atom is mostly used for actual truthiness.

elbrujohalcon added a commit that referenced this pull request Sep 23, 2025
* Improve documentation with "Avoid" vs. "Prefer" and "Quick fix" (#403)

* Approach our initial goal

* Approach our template to our current goal

* Tweak all (?) rules' documentation

* Fixes 293: Add rule: No Single Match Maybe (#412)

* elbrujohalcon.293.single_maybe_clause- New Rule: No Single Match Maybe

* elbrujohalcon.293.single_maybe_clause- Fix doc path

* elbrujohalcon.293.single_maybe_clause- Fix doc path

* elbrujohalcon.293.single_maybe_clause- Fix macro

* elbrujohalcon.293.single_maybe_clause- add feature flag

* elbrujohalcon.293.single_maybe_clause- Adjust line numbers

* elbrujohalcon.293.single_maybe_clause- Update docs to the latest style trend ✨

* elbrujohalcon.293.single_maybe_clause- Manually cherry-pick changes from #397

* Revert task name change

* Make documentation easier to follow with icons (#413)

* Fix support version range

* Make it so rules are identified in a simpler manner

* Example > Example configuration

* Sort it

* Add missing per-doc elements

* Make it easier to follow (more icons, less text)

* Fix it for consistency

* Fix per linter

* Fix post- code review

* Fix when running it on ourselves

* Same-case them (upper at start of word) and sort them

* Identify it better

* Remove non-existing ruleset

* Trim a bit frmo the top

* Add some history to the documentation (#417)

* Fix #418: New Rule: `no_nested_hrls` (#429)

* Spot some issues with `elvis.config` (non-existing modules, non-existing rules) (#397)

add elvis_config validation

* Some minor tweaks for `andalso`/`orelse` "ambiguity" (#437)

* Use parens when either A or B, from "A andalso B", is not "simple"

* Simplify it for readability

* Use parens when either A or B, from "A orelse B", is not "simple"

* Make sure A and B, from A andalso/orelse B, are both booleans

This also introduces more readability

* Tweak it around not, when close to andalso/orelse

* Consistently have , (instead of andalso) in guards

* Consistently have ; (instead of orelse) in guards

* Act on review suggestion: make it simpler

* Revert changes on parens

* Untangle expression

* Fix it

* Try with a fixed plugin version

* Propose a few changes to (the codebase's) readability and shared elements (#442)

* Introduce elvis_ktn, and arity/1

* Introduce elvis_ktn:location/1, elvis_ktn:line/1

* Introduce elvis_ktn:name/1

* rebar3 fmt it!

* Increase consistency (and reduce definitions) for is_..._node

* Approach functions

* Introduce elvis_ktn:operation/1

* Introduce elvis_ktn:text/1

* Introduce elvis_ktn:tokens/1, elvis_ktn:value/1

* Introduce elvis_ktn:function/1

* Introduce elvis_ktn:module/1

* Introduce elvis_ktn:pattern/1

* Reuse what's available

* Rename it for consistency

* Create is_..._node functions for consistency

* Further simplify the predicates

* Be consistent with 'not'

* Fix it per tests

* Revert based on self-review

* Get rid of elvis_ktn:arity/1

* Get rid of elvis_ktn:function/1

* Get rid of elvis_ktn:line/1

* Get rid of elvis_ktn:location/1

* Get rid of elvis_ktn:module/1

* Get rid of elvis_ktn:name/1

* Get rid of elvis_ktn:operation/1

* Get rid of elvis_ktn:pattern/1

* Get rid of elvis_ktn:text/1

* Get rid of elvis_ktn:tokens/1

* Get rid of elvis_ktn:value/1 (and with it, the module it's in)

* Reduce number of changes (when not relevant, except for naming)

* Move `case ... is_..._` back to `case ... ktn_code:type(...`

* Reduce to single-call functions that aren't reused

* Test an alternative on simplification

* Signal stuff for follow-up

* Make for consistency: move predicate to only place it's used

* Update some more, to get a feel of the end result

* Prefer strict equivalence

* Continue on our path to ~glory~ simplicity

* Move predication definitions closer to where they're used

* Increase consistency

* Name our predicates

* Fix `operator_spaces` for [`=`, `=>`, `:=`, `<-`, `<=`], while adding `?=` to it, too (#446)

Fix operator_spaces on [`=`, `=>`, `:=`, `<-`, `<=`]

...  while adding `?=` too

* Add `{gen_statem, call, 2}`, `{gen_server, call, 2}`, and `{gen_event, call, 3}` to option `caveat_functions` in rule `no_common_caveats_call` (#450)

Fix new function calls for No Common Caveats

* Improve `macro_module_names`' test suite (increase it with elements from a report) (#449)

Add report code to check that currently we don't have an issue

* Simplify `elvis_code` (#447)

* Remove dead code

* Simplify function_naming_convention

Drop "generic" (only used once) functions and replace
inside the _rule definition_

* Simplify god_modules, export_used_types, and max_function_arity

Drop "generic" (used 3 times) functions and replace
inside the _rule definition_

* Simplify export_used_types, and private_data_types

Drop "generic" (used 2 times) functions and replace
inside the _rule definition_

* Move some more "not so generic" code next to its implementation

* Simplify it

* Simplify for defaults

* Remove unused doc-specific indications

* Complement doc. for elvis_code

* Remove not-especially-useful doc. content from elvis_core

* Make it consistent for elvis_result

* Further simplify code in elvis_text_style

* Simplify it

* Fix `no_boolean_in_comparison`: adds [`=:=`, `/=`, `=/=`] (#448)

* Add to tests stuff that should fail (while adjusting lineno)

* Fix it per our latest expectations

* Act on dogfooding results

* Rethink it

* Simplify it further, by being sneaky (?)

* Update katana-code and remove dialyzer attribute (#452)

* Rely on `beam_files` for pre-processed -related analysis (`no_debug_call`, `invalid_dynamic_call`, ...) (#451)

* Fix `prefer_unquoted_atoms` (match better) (#455)

* Simplify matching on prefer_unquoted_atom

Avoid a list of reserved words and compares as per Erlang/OTP's atom
definition

* Act on dogfooding results

* Reduce number of differences to actual code

* New rulesets: `hrl_files_strict` and `beam_files_strict` (#453)

* Document `beam_files` and upcoming `beam_files_strict`

* Signal hrl_files (and upcoming hrl_files_strict) better

* Make it easier to maintain and reason on

We propose:
- a single function for rules (since they're now defined the
  same way in functions)
- functions ..._rules where we compose for what we want
  (no repetition)

This allows us to e.g.:
- identify HRL-only rules (which lead to a documentation update)
- that we were missing some text_style rules in the strict group
- that we known easily what doesn't work on BEAM (can compare to docs)

* Remove test that makes less sense now we're composing stuff

* "Pay" the price of making it more consistent (or tweaking it)

* Act on further test results

* Tweak it because even if available it might not be applicable

* Remove unnecessary config. option

* Fix doc

* Have elvis_ruleset (was elvis_rulesets) be a behaviour

* Expand list comprehensions to ease readability

* Sort it

* Allow for OTP behaviours to bypass `export_used_types` (#457)

* Don't repeat yourself

* Rename it for clarity

* Don't act on export_used_types for known OTP behaviours

* Lint `elvis_core` further (#456)

* Fix gitignore rules (return is not {ok, _})

* Fix per gitignore rules' results

* Allow for beam-only analysis to be more complete

* Dogfood further

This allowed us to find minor stuff we missed before

* Reuse code when possible (minor tweak/fix to `elvis_file:module/1`) (#458)

* Add `list_to_atom/1` and `binary_to_atom/1,2` to rule `no_common_caveats_call`'s option `caveat_functions` (default) (#464)

* Add list_to_atom/1 and binary_to_atom/1,2 to no_common_caveats_call

* Avoid some, fix some

* Approach it from a safer angle

* A few minor fixes around documentation and moving towards OTP 28 (#465)

* Fix doc.s (extraneous `)`)

* Fix lists as per code (increase consistency)

* Already prepare for what OTP 28 will propose we do

* Fix missing reference

* Remove macros that our support range doesn't require

* Fix #427: Improve invalid_dynamic_call (#467)

* Further code reviews on #467 (#468)

* Add `{dbg, '_'}, {dyntrace, '_'}, {instrument, '_'}` to `no_debug_call` (#471)

Increase elements we consider to be `debug_calls`

* Fix #426: New rule: no_receive_without_timeout (#472)

* Add `{left, "."}, {left, ";"}` to `no_space`; add `{right, ";"}` to `operator_spaces` (#470)

* Add `left, .` to `no_space` (default)

* Add `left, ;` to `no_space` (default)

* Add `right, ;` to `operator_spaces` (default)

* Act on self-review: more doc

* Handle left-of better

* Test further

* Support OTP 28 (#466)

* Support OTP 28

* rebar3 upgrade it!

* Simplify it

* rebar3 upgrade it!

* Add `<:=`, `<:-`, and `&&` (`left` and `right`) to rule `operator_spaces` (#463)

* Support OTP 28

* rebar3 upgrade it!

* Simplify it

* rebar3 upgrade it!

* Add support for OTP 28's newest generators in operator_spaces

---------

Co-authored-by: Brujo Benavides <[email protected]>

* Fix badge link and support range (#474)

* Alternative fix for #476 (#479)

* Fix #481: Improve comparison for no_operation_on_same_value (#483)

* Fix #484: Properly show include filenames in no_nested_hrls (#485)

* Attempt to fix crash in export_used_types (#489)

* Propose a more consistent output message format (and hopefully simpler coding of future rules) (#475)

* Add forbidden_regex to macro_names (#492)

* Move `& `  to `$ `  in documentation (#502)

Move `& ` to `$ ` in documentation

* Tweak: further simplification in the code base (#500)

* Simplify it for default values

* Allow for new find_by_types/4 (with filter)

* Simplify further patterns

* Continue the pursuit for patterns

* Fix doc where lacking

* Update/remove spec/type where unwarranted

* Stay the course of simplification

* Propose a way to simplify writing rules: first pass

* Find further defaults and common patterns we can tweak

* Propose a way to simplify writing rules: second pass

* New format: no_nested_hrls

* New format: no_specs

* New format: no_block_expressions (introduces tokens_as_content/1)

* New format: no_macros

* New format: macro_names

* New format: no_types

* New format: no_if_expressions

* Fix elvis_code:find/1 (typespec) and stop reversing list

Results are already sorted at the end, in any case

* Tweak specs a bit (add more zipper support)

I want to review no_opaque at the end alone

* New format: used_ignored_vars

* New format: no_behavior_info

* New format: invalid_dynamic_call

* New format: no_spec_with_records

* Minor format changes

* New format: max_function_clause_length

* Fix reading options for max_function_clause_length

We're already using ?FUNCTION_NAME elsewhere and this is a good
way of avoiding this category of issues

* New format: max_function_length

* Tweak a few messages (make limits more explicit)

* New format: no_nested_try_catch

* New format: no_successive_maps

* Simplify for already-tested condition

* New format: no_init_lists

* New format: ms_transform_included

* New format: no_boolean_in_comparison

* New format: no_receive_without_timeout

* New format: no_operation_on_same_value

* New format: no_throw

* New format: no_dollar_space

* New format: no_author

* New format: no_import

* New format: no_catch_expressions

* New format: no_single_clause_case

* New format: behaviour_spelling

* New format: param_pattern_matching

* New format: consistent_generic_type

* New format: always_shortcircuit

* Remove unnecessary reference to generic doc.

* New format: module_naming_convention

* New format: max_module_length

* New format: max_anonymous_function_arity

* New format: max_function_arity

* New format: no_single_match_maybe

* New format: no_match_in_condition

* New format: export_used_types

* New format: private_data_types

* Prevent copy-paste errors (and also tightly couple rule and rule name)

* Vastly simplify the :find API

* Review code that was left for review later

* New format: function_naming_convention

* New function: variable_naming_convention

* New format: atom_naming_convention

* Minor tweaks to comments and docs

* Improve in preparation for further tweak operations

* Let rebar3 control the output of compilation errors

* New format: no_space_after_pound, no_space, operator_spaces

* New format: nesting_level

* Handle regex in similar manner across the board

We implement:

1. pre-compilation for faster results
2. tweaks to outputs, that stemmed from test on oddities
3. a re-invention of forbidden_regex for macro_names

* Allow Dialyzer to know when we're breaking opacity or not

* New format: god_modules

* New format: state_record_and_type

* New format: no_call, no_debug_call, no_common_caveats_call

(via no_call_common)

* New format: numeric_format

* New format: consistent_variable_casing

* Put it back again: concentrate on important stuff

* New format: dont_repeat_yourself (and drop some specs)

* Fix printing dont_repeat_yourself output

* Act on self-review

* Finalise it for review

* Fix it for OTP 26

* Act on self-review

* Make it more explicit where we find compulsory-ness

* code_zipper > zipper, as per review comment

* Improve (internal) API consistency, as per review comment

* Make it more generic, as per review comment

* Trim it, as per review comment

* Simplify it, as per review comment

* Remove dead code, as per review comment

* Remove function, as per review comment

* Rename some of the rules (#505)

* Rename god_modules as no_god_modules

* Rename nesting_level as no_deep_nesting

* Rename invalid_dynamic_call as no_invalid_dynamic_calls

* Rename used_ignored_variable as no_used_ignored_variables

* Rename macro_names as macro_naming_convention

* Rename consistent_generic_type as generic_type

* Rename consistent_variable_casing as variable_casing

* Deprecate old_configuration_format

* Minor fix for better visual results

* Update README examples per more recent output

* Drop src_dirs > dirs normalization as pre-1.0.0 worry

* Identify behavior-related exports closer to home

* Make reusable stuff reusable (and tweak variable names)

* Drop "map not list" config. normalization as pre-1.0.0 worry

* Drop seemingly dead code (I searched elvis and rebar3_lint, too)

* Fix typo

* Renaming for clarity around ruleset/rule

* Move non-generic stuff out of "generic" module

* Distinguish between rule set and rule namespace

These are mentioned in the README and should easily be
distinguished:

- a rule namespace is just a module implementing a rule definition
- a rule set is a number of rules, from any module, with a given
  config

* Make rules opaque

* Tweak the README

I'll later implement a warning around this syntax
while keeping the previous behavior

* Tweak some of our types

* Act on review comments

* #462: New Rule: guard_operators (#506)

* [#462] Add docs and tests

* Add [some] code

* Fix #462: New rule: guard_operators

* Fix rebase

* Adjust for OTP26

* Reduce line length

* Don't test on beam_files rules that are OTP27+

* Revert bad changes

* #462 guard_operators fixes (#507)

* Add ruleset for tests(#435)

Add a new ruleset, designed for test files
This ruleset now includes all of the default `.erl` rules, except `dont_repeat_yourself` and `no_god_modules`.
Run Elvis on test files.

* #461: New rule: simplify_anonymous_functions (#511)

* #461: New rule: simplify_anonymous_functions

* Remove unneeded clarification

* Simplify code even further

* Update doc_rules/elvis_style/simplify_anonymous_functions.md

Co-authored-by: Paulo F. Oliveira <[email protected]>

* Update src/elvis_style.erl

Co-authored-by: Paulo F. Oliveira <[email protected]>

* Fix md

---------

Co-authored-by: Paulo F. Oliveira <[email protected]>

* Allow catch expressions if the result is discarded (#512)

* Allow catch expressions if the result is discarded

* Improve documentation

* Move prefer_unquoted_atoms to elvis_style (#513)

* Move prefer_unquoted_atoms to elvis_style

* Note about previous rule NS

* #487 New rule: Prefer Include (#514)

* Rename no_nested_hrls as no_includes (#519)

* Rename no_nested_hrls as no_includes

* Remove unneeded text

* Handle named funs in all rules (#523)

* Fix #431: New strict rule: strict_term_equivalence (#524)

* Fix #431: New strict rule: strict_term_equivalence

* 🐩

* Add more tests

* New Rules for anonymous function lengths (#522)

* New Rules for anonymous function lengths

* Adjust defaults

* Fixes from #532 review (#525)

* Apply all the OTP24 patches

---------

Co-authored-by: Paulo F. Oliveira <[email protected]>
Co-authored-by: Bór Milán <[email protected]>
Co-authored-by: Benedikt Reinartz <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug in no_boolean_in_comparison?

5 participants