Skip to content

What-if: implicit bool coercion in if-conditions (truthiness) #94

@tmteam

Description

@tmteam

Summary

Currently if conditions require exactly Bool (SetOrCreatePrimitive(condId, StatePrimitive.Bool) in GraphBuilderExtensions.cs:31). This proposal loosens the constraint to the interval [bool..any]:bool! — named Truthy.

Truthy = interval [bool..any]:bool!

The types that can theoretically be true at runtime are exactly the supertypes of bool up to any:

any          ✓  (upper bound)
  some       ✓
  bool?      ✓  (bool ≤ bool? ≤ any)
  bool       ✓  (lower bound, preferred)
  ──────────────
  int        ✗  (not a supertype of bool)
  text       ✗  (different branch)
  int[]      ✗  (different branch)
  struct     ✗  (different branch)

Runtime — use existing polymorphic ==:

if expr compiles to if (expr == true). No runtime type checks, no special unwrap:

value AreEqual(value, true) result
true (bool) true == true true
false (bool) false == true false
null (none/bool?) null == true false
5 (any holding int) 5 == true false

Even bool? needs no unwrap — AreEqual(null, true) → false naturally.

Generic function inference

fun foo(expr) = if expr 1 else 0

Pros

Hard optional ergonomics

a:bool? = getSomeComplexResult()

if(a == true)   # annoying boilerplate every time
    ...

if a            # just works — bool? is Truthy, none → false
    ...

Without Truthy, every optional bool condition requires explicit == true or a ?? false. With optional types everywhere, this becomes pervasive and irritating.

Scripting naturalness

a:any = true

if(a == true)   # why not just `if a`?
    ...

if a            # natural, expected behavior for script users
    ...

Historical precedent

C/C++ established if as a truthiness check — if(x) meant x != 0. Most languages since then accept some form of implicit condition. Requiring exact Bool feels overly strict for an expression language.

Cons

Semantic ambiguity: == true vs "exists"

result = getData()   # returns {value: 42} or none
if result            # user EXPECTS: true (data exists)
                     # ACTUAL: false (struct != true)

People from Python/JS will assume if x means "x is truthy/exists". In NFun it means x == true. A struct, an array, a non-zero int — all false. This WILL confuse users. Must be clearly documented: if xif (x == true), not "if x exists".

and/or consistency problem — THE BIG QUESTION

a:bool? = ...
b:bool? = ...

if(a and b)   # What is the result type? What does the user expect?

If if accepts Truthy, should and/or also accept Truthy operands?

Script user expects: none and true → false (none is falsy, short-circuit)
Strict user expects: none and true → compile error (bool? is not bool)

And it cascades further:

  • Are and/or now (Truthy, Truthy) → bool?
  • What about not? Is not a:any valid? not (a == true)?
  • What about arithmetic in conditions: if(count and hasItems) — if count:int, it's rejected (not Truthy). Consistent? Or confusing to C programmers?

Consistency risk: if if accepts [bool..any] but and/or stay exact bool, then:

if a:bool?              # ✓ works
if a:bool? and b:bool?  # ✗ error — and requires exact bool

This is inconsistent and surprising. But making and/or Truthy opens a whole new can of worms about return types and semantics.

not semantics

a:any = 42
if not a    # not(42 == true) → not(false) → true?
            # or: not(42) → compile error (not requires bool)?

Scope

Truthy constraint applies to:

  • if conditions
  • while conditions
  • Open question: and, or, not operands

Comparison with other languages

Language if accepts NFun difference
Python, JS, Ruby any type (value-based truthiness) NFun: only types where true IS POSSIBLE
Kotlin, Swift Bool only NFun: also bool?, any, some
C/C++ numeric (0 = false) NFun: no numeric truthiness

Summary

Total implementation cost:

  1. TIC: change one constraint (interval instead of exact primitive)
  2. Runtime: one AreEqual(value, true) call using existing == operator

Metadata

Metadata

Assignees

No one assigned

    Labels

    TicType inference calculator has to be changedWhat if?[Candidate]

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions