[HN Gopher] Imagining a language without booleans
       ___________________________________________________________________
        
       Imagining a language without booleans
        
       Author : todsacerdoti
       Score  : 71 points
       Date   : 2025-09-22 22:09 UTC (4 days ago)
        
 (HTM) web link (justinpombrio.net)
 (TXT) w3m dump (justinpombrio.net)
        
       | Aardwolf wrote:
       | So basically, C before version C99
        
         | kevin_thibedeau wrote:
         | Technically C23. _Bool is just a fancy substitute for unsigned
         | char and "casting" with the !! sequence.
        
       | Y_Y wrote:
       | https://en.wikibooks.org/wiki/Haskell/Understanding_monads/M...
       | 
       | This looks homeomorphic to the Maybe monad.
        
         | nh23423fefe wrote:
         | isomorphic?
         | 
         | homeo implies continuity
        
           | Y_Y wrote:
           | And continuous inverse! I was just being silly, you're right
           | of course.
        
       | Retr0id wrote:
       | funny, I've been toying with the idea of a language with _only_
       | booleans
        
         | f1shy wrote:
         | Wouldn't that just be logic gates, Mealy and Moore machines
         | written in text form?
        
           | Retr0id wrote:
           | Yes. Except, the language should be expressive enough that
           | you can build abstractions and write "normal" code. And, the
           | compiler should be smart enough to emit efficient native code
           | for your target arch (and not doing FPGA-style hardware
           | synthesis)
        
           | zoky wrote:
           | Well, that's true and false.
        
       | seanhunter wrote:
       | Imagine a language that not only doesn't have booleans, it only
       | has positive fractions. It also doesn't have any keywords, yet it
       | is Turing-complete.
       | 
       | Struggling to imagine it? Don't worry. John H Conway has done it
       | for you.
       | 
       | https://en.wikipedia.org/wiki/FRACTRAN
        
         | entaloneralie wrote:
         | In FRACTRAN, Bools is all you get.
         | 
         | The value is 6 is a:true, b:true, c: false, d: false, etc..
         | 
         | I love fractran tho, I must have written a thousand lines of it
         | this year alone.
        
       | nh23423fefe wrote:
       | Not really without. More like obscured booleans, 2 embeds into
       | T+1 which embeds into T+U. I mean C used the sign bit to do the
       | same.
        
       | brador wrote:
       | Booleans are a remnant of limited RAM.
       | 
       | 2025, we can all just use integers and carry one less variable
       | type in our sack.
       | 
       | Next we replace integers with floats and we're done. 3 birds, 1
       | stone.
        
         | legacynl wrote:
         | We could just use 2 integers for floats and not deal with
         | inaccuracies. Even easier.
        
           | aDyslecticCrow wrote:
           | Floats can represent faaar bigger span than two integers.
           | 2*10^-308 to 2*10^+308 is gonna require you 1024 bits or 32
           | integers. Now those 32 integers would represent that value
           | exactly instead of rounding a few decimals, but some maths
           | prefer approximate large span over exact values.
        
         | aDyslecticCrow wrote:
         | > Booleans are a remnant of limited RAM.
         | 
         | I highly doubt that. Let's call a boolean that is represented
         | as one bit "true boolean type" Since no instruction set (that
         | i'm aware of) has boolean operators, a "true boolean" would
         | require every operation on it to evaluate to multiple bit-wise
         | operations, which take up registers and cycles. Flags in
         | registers are "true boolean", but they're usually operated on
         | explicitly like int with bit-wise operators.
         | 
         | There is also the issue of bit alignment, atomic access, and
         | stack and heap allocations being byte based; further
         | restricting how a language that had "true booleans" would be
         | able to actually be able to work with them.
         | 
         | I know that there are some languages that allow boolean arrays
         | to be packed tightly as "true boolean", but that is a rare
         | exception. Even char and byte types has this issue sometimes,
         | but are more commonly "properly packed".
         | 
         | > we can all just use integers
         | 
         | So it's all integers already. The most common implementation of
         | boolean around is probably #define true 1
         | 
         | But we really should use enums more instead of boolean. "fail,
         | success, bad_param, error_404" is equally efficient to return
         | as a bool.
         | 
         | > Next we replace integers with floats.
         | 
         | No. (well python and JavaScript kinda does already, but no)
         | https://en.wikipedia.org/wiki/Pentium_FDIV_bug
        
           | Dylan16807 wrote:
           | Your argument against floats is a bug from 30 years ago?
           | That's probably the weakest possible reason you could have
           | given.
        
         | GLdRH wrote:
         | Why not only have strings? "zero", "one", "two", ...
         | "threepointonefourone..."
        
           | CorrectHorseBat wrote:
           | Congratulations, you have invented tcl
        
           | iberator wrote:
           | Perl, awk, tcl, bash - all string based langs. Especially TCL
        
         | jbreckmckye wrote:
         | Floats are an excellent choice because they encode the
         | inevitable ambiguity of the world. Instead of true and false we
         | can have                   true    = 1         false   = 0
         | perhaps = 0.5
        
           | mattkrause wrote:
           | And now you've invented Church (among others)!
        
           | inkyoto wrote:
           | Addendum: <<nevermore == -1>> for <<no value>>.
        
           | jodrellblank wrote:
           | Tracking the accumulated 'perhaps'ness... "Essentially, each
           | Ball Arithmetic number carries with it an explicit Radius
           | that describes the interval which contains the true value.
           | Because Ball Arithmetic data propagates throughout a
           | calculation and is never demoted to a lower datatype, its
           | advantage is that after each operation the resulting Ball
           | reliably includes the true answer. Across an entire
           | algorithm, the final result is a Ball that indicates
           | explicitly how precise it is, thus eliminating the guesswork
           | as to the effect on the algorithm of the inexactness of the
           | input and any other inexact constants used"
           | 
           | - NARS2000 dialect of APL, Ball / Interval arithmetic:
           | https://wiki.nars2000.org/index.php/Ball_Arithmetic
        
         | immibis wrote:
         | We could just use strings for everything, like bash, and tcl,
         | and the weird scripting language embedded in every mid-2000s
         | FPS game.
        
         | nomel wrote:
         | The nice part about floats is the bit packing is usually
         | standard, meaning you can have them hold arbitrary data, if you
         | sacrifice a couple of bits!
         | 
         | There's a robot controller that I work with that has a large
         | number of calibration tables, of float32 values. This is where
         | I store the json! :D
        
       | munificent wrote:
       | Really cool post. I've had roughly similar thoughts when noodling
       | on my current hobby language [1], but didn't work all the way
       | through it to see if it hangs together. It seems like it might!
       | 
       |  _> Let me know if you've seen anything more similar._
       | 
       | If you take static typing off the table, then Icon's goal-
       | directed execution is very much an inspiration in this area.
       | 
       | [1]: https://journal.stuffwithstuff.com/2023/01/03/type-
       | checking-...
        
         | cryptonector wrote:
         | And Verse.
        
           | NetMageSCW wrote:
           | Which was mentioned in the original article.
        
         | NetMageSCW wrote:
         | Icon was the first thing I thought of as well. I wonder about
         | Prolog...
        
       | jbreckmckye wrote:
       | Now that we have defined a language without booleans, we need
       | some way to coalesce these optional values
       | 
       | We ideally want an infix function that can reduce the
       | "truthiness" of two values.
       | 
       | Let us imagine this language is a Haskell-type-thing, and we can
       | define pseudo-operators with pattern matching
       | infixr 3 &&         (&&) :: Optional -> Optional -> Optional
       | Empty && _       = Empty         _ && Empty       = Empty
       | Some A && Some B = Some A         _ && _           = Empty
       | infixr 2 ||         Some || _     = Some         None || Some  =
       | Some         None || None  = None         _    || _     = None
       | 
       | Hmm, let's see how that looks                   a = b && c
       | d = e || f
       | 
       | The good news is that we are free from the tyranny of booleans.
       | The bad news is that we just reinvented JavaScript :-)
        
         | BriggyDwiggs42 wrote:
         | I honestly really like those two operators in js.
        
           | melncat wrote:
           | Don't forget about ?? as well
        
             | jbreckmckye wrote:
             | I'm a big fan of the Elvis operator myself (.?)
        
               | chuckadams wrote:
               | .? is null-safe property access. Elvis is ?:
        
               | jbreckmckye wrote:
               | Oh, that makes more sense, because ?: has eyes
               | 
               | https://en.m.wikipedia.org/wiki/Elvis_operator
               | 
               | ?:-)
        
             | mock-possum wrote:
             | Mmm nullish coalescing
        
         | kmill wrote:
         | That second operator is the <|> operator, from the Alternative
         | typeclass.
         | 
         | The first one has some arbitrariness (do you take the left or
         | right value if both are Just). But, thankfully the Applicative
         | typeclass gives both <* and *>, which lets you choose which
         | value you want:                 Just A <* Just B = Just A
         | Just A *> Just B = Just B
         | 
         | (There's the possibility to merge values too, with f <$> Just A
         | <*> Just B, which evaluates to Just (f A B). I feel like this
         | is a "don't try to understand it, just get used to it" sort of
         | syntax. It can be pretty convenient though.)
        
         | zahlman wrote:
         | > The bad news is that we just reinvented JavaScript :-)
         | 
         | There's a whole lot more to JavaScript typing that makes it
         | JavaScript.
         | 
         | After all, Python does this too (but the spellings are "and"
         | and "or").
        
         | Twey wrote:
         | The big difference being that 'truthiness' is explicitly
         | encoded next to the value rather than being an inherent
         | property of certain values. That's a win in my book!
        
       | legacynl wrote:
       | I don't really get it.. In one of the last example's he writes:
       | 
       | ` if (node.last_child(s) is Ok(last_child))`
       | 
       | Is the part between the () not ultimately the same as a boolean
       | expression? Like he wrote his own implementation of if/else
       | syntax?
       | 
       | Also in the beginning he says: "An if with an else can produce a
       | value", but isn't this just 'syntactic sugar'? I think the code
       | that actually runs is the same as if you'd write if (value x =
       | some_value) {value = something} else {value = something_else} ?
        
         | myhf wrote:
         | This concept doesn't require the Law of Excluded Middle that
         | classical boolean values do.
        
           | legacynl wrote:
           | Ah alright, since I don't know what that is I will attribute
           | it to my own lack of knowledge then.
        
             | IAmBroom wrote:
             | Basically, the Excluded Middle is for things neither True
             | nor False.
        
               | legacynl wrote:
               | Yeah okay I get it. The law basically states that 'not
               | true' should be 'false' and vice versa.
               | 
               | I still don't get what's the use of this, or is this just
               | a curiosity? It seems like the result is just a kind of
               | ternary operator? Doesn't this still just compile to
               | if(x.present) return x else y? Just with really obtuse
               | syntax
        
               | stonemetal12 wrote:
               | If you are doing proofs then the law of the excluded
               | middle creates paradoxes. "this statement is false" is a
               | paradox with the law but isn't without the law. In a
               | programming language not used for proofs? Then yeah it is
               | just for fun.
        
       | asplake wrote:
       | Perhaps it's cheating, but last I checked, Gleam has no "if",
       | only "match". With that, and in languages with sum types, you can
       | easily define your own boolean and boolean-adjacent types.
        
         | kayodelycaon wrote:
         | If null, you have booleans: null or not null. :)
        
       | rhaps0dy wrote:
       | Basically Emacs Lisp. Where `nil` is the only falsy value, and
       | anything else (including `t`, which analogizes to `Some(())`) are
       | truthy values.
        
       | nahumba wrote:
       | Headline no bit. Then attack if.
       | 
       | There would be no branching if there were no "if". It's basic
       | assembly. Not jumps. No loops.
        
         | 1718627440 wrote:
         | Yes, I think it really only works in a side-effect free
         | language. Otherwise after the first "failure", while every
         | resulting value also turns to None, allocation, file access,
         | database calls still happen, so you are now in a weird limbo
         | state where everything was done, but no handle retained and in
         | a weird position in control flow, kind-of between the lines,
         | which isn't expressed by any of your code.
        
       | moltar wrote:
       | Perl
        
       | weatherlight wrote:
       | I can. it's called Erlang. true an false are just atoms.
        
       | jerf wrote:
       | Unfortunately, you're invariably going to end up with a
       | "Some<false>" at some point, and you're going to spend the next
       | 20 years explaining to people why that's not a wart in your
       | language that your if "treats it as true", no matter how much you
       | say "my language doesn't even have true so that's not a valid
       | statement".
       | 
       | It isn't going to matter that it's technically a "JSON::false" or
       | whatever... you're still going to get people calling that a wart
       | forever. ("But a JSON::false would be a None" - no, you need that
       | for either "null" or "missing". A JSON library has to have an
       | exposed "false" value to distinguish it from null and missing,
       | both for input and output.)
       | 
       | I'm not saying that doesn't mean to try this out, but as more of
       | a heads up and something to talk about explicitly in the eventual
       | tutorial.
       | 
       | Personally, I find myself fairly satisfied with if statements
       | rigidly requiring a boolean and refusing to have a concept of
       | "truthiness", which I consider a mistake, and I'm not sure this
       | is solving real problems I've had. A user can always write the
       | Option vs. None themselves in an if statement with mandatory else
       | if they want. This introduces a wrapper level of Option that may
       | not always play nice in real code (I mean, a lot of sum type
       | types essentially already have it built in with things like "type
       | Color = Red | Blue | Green | Unspecified" where adjoining a None
       | is often unnecessary) and may really tempt you towards a concept
       | of truthiness that may be a bigger wart than when you're trying
       | to fix. It's pretty hard for a computer programming language to
       | essentially evict the concept of a "bit" from the language. I'm
       | not sure it can be done in practice, but it's fun to think about
       | it and I encourage the pondering.
        
         | moritzwarhier wrote:
         | I admit I didn't read all of your commment.
         | 
         | But is                 Some<false>
         | 
         | different from                 Maybe<false>
         | 
         | ? Is it a type-level construct to express a predicate like
         | Array.prototype.some
         | 
         | ?
         | 
         | In any case, optional types containing boolean values are
         | definitively an anti-pattern.
         | 
         | And in cases where it's prudent to check for the "presence" of
         | a property containing a                 Maybe<ObjectReference>
         | 
         | , while using coercion, it does not make sense to distinguish
         | false from "falsy".
         | 
         | TypeScript's dynamic narrowing has become pretty comprehensive
         | when it comes to this kind of issue.
         | 
         | Still, Option<Boolean> types are bad in most contexts I think,
         | especially in languages like JS.
         | 
         | Instead of using boolean | undefined , I much prefer explicit
         | defaults (parameters) or properties being declared every time
         | (data).
        
           | jerf wrote:
           | Some<false> was written as the value; the type of that value
           | would be Option<(Some Sort Of Still-A-Boolean)>. I have to
           | write it that way because the entire conversation is about
           | the language not having a boolean, so you can't properly
           | write Option<bool> as a type in that language, which is why I
           | mention it may actually be JSON::boolean or something, as
           | well as several other booleans in other similar situations.
           | My point is that you'll still end up with booleans in them
           | though because you'll still have to have representations of
           | _other_ thing 's booleans anyhow.
        
             | moritzwarhier wrote:
             | Thanks for coming back to my comment and explaining!
        
       | elcapitan wrote:
       | Too good to be true or false
        
       | taylorallred wrote:
       | I love seeing these kinds of explorations in the realm of
       | language design. I've wondered about expanding the notion of
       | boolean operators like this. For all its flaws, one thing I've
       | always liked about JS is the overloaded (||) and (&&) operators.
       | It's really slick to write something like
       | `foo.get_that_can_fail(x) || "default value"`.
        
       | kazinator wrote:
       | > Can we go further
       | 
       | Yes. we can get a Lisp background to get a better rounded and
       | wiser perspective on all these topics.
        
       | kayodelycaon wrote:
       | I'm pretty sure Ruby already does this. if statements are
       | expressions that return a value and return nil if there is no
       | else.
       | 
       | (Also 0 == true in Ruby. Only false or nil are not true.)
        
       | jayd16 wrote:
       | If you're looking to imagine a world without bools, do some
       | branchless gpu shader coding. Certainly its a different way to
       | think about processing data.
        
         | jbreckmckye wrote:
         | I've also seen (non-GPU) programs use numbers with
         | multiplication, instead of if-else, as a means of preventing
         | CPU branch stalls. This technique is sometimes called
         | branchless programming.
        
         | nomel wrote:
         | > do some branchless gpu shader coding
         | 
         | A mask array is just a bunch of boolean, and follows boolean
         | math, because it _is_ boolean values. A boolean type does not
         | make a boolean value, it being two state does.
        
       | jujube3 wrote:
       | Most dialects of BASIC didn't have booleans.
       | 
       | Everything old is new again.
        
         | SAI_Peregrinus wrote:
         | And most assembly languages don't have general-purpose
         | booleans. And C89 doesn't have booleans. And FORTHs don't have
         | booleans in the base interpreter, those are words defined like
         | any other.
        
         | NetMageSCW wrote:
         | Most BASIC dialects map booleans to integers with zero is
         | false, non-zero is true and -1 is the canonical true result of
         | boolean operators, so I think they effectively have booleans.
        
       | cryptonector wrote:
       | This:                 bool  = nil?nil              true  =
       | Ok(nil)       false = Err(nil)
       | 
       | is still booleans.
       | 
       | Icon and Verse use failure (backtracking!) as false and all
       | values as true, but you still have conditionals and boolean logic
       | even though you don't (or may not) have a boolean type.
        
       | Twey wrote:
       | Definitely a fun alternative-history! It's a nice take to see
       | `Option`s or `Result`s as a step towards logic programming.
       | Typically once you introduce loops you need to have a way to
       | combine `E`s; the way to represent an ordered set of things e.g.
       | to be combined is to return a list of things, and then you're in
       | logic programming world. [1]
       | 
       | [1]: https://wiki.haskell.org/Logic_programming_example
       | 
       | It's a bit weird to me that the result `not` discards the content
       | of the value rather than just swapping its truthiness (not A?E :
       | E?A).
       | 
       | > The closest thing I've seen is fallible expressions in Verse,
       | but those are pretty different because they (i) don't assign a
       | value to an if without an else, and (ii) involve speculative
       | execution.
       | 
       | Traditional ifs, and the ifs here, also involve speculative
       | execution :) (i.e. execution that happens regardless of which
       | branch you end up on). It's just delimited by the brackets of the
       | if condition (a 'failure context' in Verseland). It's true that
       | traditionally logic languages don't assign a value to failure. I
       | guess algebraic effects (of which `Result` can be an example) can
       | be seen as a generalization of failure in that way.
       | 
       | Amr Sabry &a. also have an interesting notion of 'failure with
       | value' as the semantics of their negative types:
       | https://dl.acm.org/doi/abs/10.1145/3434290 , http://lambda-the-
       | ultimate.org/node/4964 (LtU for an older paper from the
       | programme).
        
       ___________________________________________________________________
       (page generated 2025-09-26 23:01 UTC)