-
Notifications
You must be signed in to change notification settings - Fork 6
Description
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 x ≡ if (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/ornow(Truthy, Truthy) → bool? - What about
not? Isnot a:anyvalid?not (a == true)? - What about arithmetic in conditions:
if(count and hasItems)— ifcount: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:
ifconditionswhileconditions- Open question:
and,or,notoperands
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:
- TIC: change one constraint (interval instead of exact primitive)
- Runtime: one
AreEqual(value, true)call using existing==operator