Skip to content(if available)orjump to list(if available)

Imagining a language without booleans

Imagining a language without booleans

40 comments

·September 22, 2025

taylorallred

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"`.

jbreckmckye

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 :-)

kmill

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.)

BriggyDwiggs42

I honestly really like those two operators in js.

melncat

Don't forget about ?? as well

jbreckmckye

I'm a big fan of the Elvis operator myself (.?)

jerf

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

I admit I didn't read all of your commment.

But is

  Some<false>
different from

  Maybe<false>
?

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 constraining all such booleans to have a default of false, they should always have an explicit default (parameters) or be declared every time (data).

legacynl

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

This concept doesn't require the Law of Excluded Middle that classical boolean values do.

legacynl

Ah alright, since I don't know what that is I will attribute it to my own lack of knowledge then.

IAmBroom

Basically, the Excluded Middle is for things neither True nor False.

Y_Y

https://en.wikibooks.org/wiki/Haskell/Understanding_monads/M...

This looks homeomorphic to the Maybe monad.

nh23423fefe

isomorphic?

homeo implies continuity

Y_Y

And continuous inverse! I was just being silly, you're right of course.

munificent

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-...

elcapitan

Too good to be true or false

rhaps0dy

Basically Emacs Lisp. Where `nil` is the only falsy value, and anything else (including `t`, which analogizes to `Some(())`) are truthy values.

weatherlight

I can. it's called Erlang. true an false are just atoms.

Aardwolf

So basically, C before version C99

seanhunter

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

asplake

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.