Publish boolean values post.
This commit is contained in:
parent
6c3780d9ea
commit
06cbd93f05
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: "How Many Values Does a Boolean Have?"
|
||||
date: 2020-08-20T18:37:50-07:00
|
||||
draft: ["Java", "Haskell"]
|
||||
date: 2020-08-21T23:05:55-07:00
|
||||
tags: ["Java", "Haskell", "C and C++"]
|
||||
---
|
||||
|
||||
A friend of mine recently had an interview for a software
|
||||
|
@ -38,12 +38,12 @@ in Haskell, and `1` and `0` in C. A boolean value is either true or false.
|
|||
So, what's there to think about? There are a few things, _ackshually_.
|
||||
Let's explore them, starting from the theoretical perspective.
|
||||
|
||||
### What's a Type, Anyway?
|
||||
### Types, Values, and Expressions
|
||||
Boolean, or `bool`, is a type. Broadly speaking, a type
|
||||
is a property of _something_ that defines what the _something_
|
||||
means and what you can do with it. That _something_ can be
|
||||
several things; for our purposes, it can either be an
|
||||
_expression_ in a programming language (in the form of `fact(n)`)
|
||||
_expression_ in a programming language (like those in the form `fact(n)`)
|
||||
or a value in that same programming language (like `5`).
|
||||
|
||||
Dealing with values is rather simple. Most languages have finite numbers,
|
||||
|
@ -81,7 +81,7 @@ int meaningOfLife() {
|
|||
|
||||
No, wait, doesn't say "stack overflow" just yet. That's no fun.
|
||||
And anyway, this is technically a tail call, so maybe our
|
||||
C++ compiler can avoid growing the stack And indeed,
|
||||
C++ compiler can avoid growing the stack. And indeed,
|
||||
flicking on the `-O2` flag in this [compiler explorer example](https://godbolt.org/z/9cv4nY),
|
||||
we can see that no stack growth is necessary: it's just
|
||||
an infinite loop. But `meaningOfLife` will never return a value. One could say
|
||||
|
@ -131,7 +131,7 @@ It turns out to be convenient -- formally -- to treat the result of a diverging
|
|||
as its own value. This value is usually called 'bottom', and written as \\(\\bot\\).
|
||||
Since in most programming languages, you can write a nonterminating expression or
|
||||
function of any type, this 'bottom' is included in _all_ types. So in fact, the
|
||||
set of possible values for `unsigned int`: \\(\\bot, 0, 1, 2, ...\\) and so on.
|
||||
possible values of `unsigned int` are \\(\\bot, 0, 1, 2, ...\\) and so on.
|
||||
As you may have by now guessed, the same is true for a boolean: we have \\(\\bot\\), `true`, and `false`.
|
||||
|
||||
### Haskell and Bottom
|
||||
|
@ -173,7 +173,7 @@ They can then compile their program; the typechecker will find any mistakes
|
|||
they've made so far, but, since the type of `undefined` can be _anything_,
|
||||
that part of the program will be accepted without second thought.
|
||||
|
||||
The language `Idris` extends this practice with the idea of typed holes,
|
||||
The language Idris extends this practice with the idea of typed holes,
|
||||
where you can leave fragments of your program unwritten, and ask the
|
||||
compiler what kind of _thing_ you need to write to fill that hole.
|
||||
|
||||
|
@ -185,8 +185,8 @@ really count as a value, since it's just a nonterminating
|
|||
expression. What you're doing is a kind of academic autofellatio.
|
||||
|
||||
Alright, I can accept this criticism. Perhaps just calling a nonterminating
|
||||
function a value _is_ far-fetched (even though denotational semantics
|
||||
_do_ extend types with \\(\\bot\\)). But denotational semantics is not
|
||||
function a value _is_ far-fetched (even though in [denotational semantics](https://en.wikipedia.org/wiki/Denotational_semantics)
|
||||
we _do_ extend types with \\(\\bot\\)). But denotational semantics are not
|
||||
the only place where types are implicitly extend with an extra value;
|
||||
let's look at Java.
|
||||
|
||||
|
@ -195,13 +195,14 @@ core language level, any function or method that accepts a class can also take `
|
|||
if `null` is not to that function or method's liking, it has to
|
||||
explicitly check for it using `if(x == null)`.
|
||||
|
||||
Java's booleans are not, at first glance, classes. Unlike classes, which you have
|
||||
This `null` value does not at first interact with booleans.
|
||||
After all, Java's booleans are not classes. Unlike classes, which you have
|
||||
to allocate using `new`, you can just throw around `true` and `false` as you see
|
||||
fit. Also unlike classes, you can't assign `null` to a boolean value.
|
||||
The trouble is, the _generics_ part of Java, which allows you to write
|
||||
fit. Also unlike classes, you simply can't assign `null` to a boolean value.
|
||||
|
||||
The trouble is, the parts of Java dealing with _generics_, which allow you to write
|
||||
polymorphic functions, can't handle 'primitives' like `bool`. If you want to have an `ArrayList`
|
||||
of something, that something _must_ be a class.
|
||||
|
||||
But what if you really _do_ want an `ArrayList` of booleans? Java solves this problem by introducing
|
||||
'boxed' booleans: they're primitives wrapped in a class, called `Boolean`. This class
|
||||
can then be used for generics.
|
||||
|
@ -219,7 +220,7 @@ Boolean myFalse = false;
|
|||
Boolean myBool = null;
|
||||
```
|
||||
|
||||
Beautiful, isn't it? And, unlike Haskell, where you can't _really_
|
||||
Beautiful, isn't it? Better yet, unlike Haskell, where you can't _really_
|
||||
check if your `Bool` is `undefined` (because you can't tell whether
|
||||
a non-terminating computation is as such), you can very easily
|
||||
check if your `Boolean` is `true`, `false`, or `null`:
|
||||
|
@ -249,17 +250,20 @@ while(test) test -= 1;
|
|||
|
||||
This loop will run 255 times, thereby demonstrating
|
||||
that C has at least 255 values that can be used
|
||||
to represent the boolean `true`. There are other languages
|
||||
with the notion of 'truthy' and 'falsey' values. However,
|
||||
some of them differ from C in that they also extend this notion
|
||||
to apply to equality. In JavaScript:
|
||||
to represent the boolean `true`.
|
||||
|
||||
There are other languages
|
||||
with this notion of 'truthy' and 'falsey' values, in which
|
||||
something not exactly `true` or `false` can be used as a condition. However,
|
||||
some of them differ from C in that they also extend this idea
|
||||
to equality. In JavaScript:
|
||||
|
||||
```JavaScript
|
||||
console.assert(true == 1)
|
||||
console.assert(false == 0)
|
||||
```
|
||||
|
||||
Then, there are still exactly two distinct values
|
||||
Then, there are still exactly two distinct boolean values
|
||||
modulo `==`. No such luck in C, though! We have 256 values that fit in `unsigned char`,
|
||||
all of which are also distinct modulo `==`. Our boolean
|
||||
variable can contain all of these values. And there is no
|
||||
|
@ -269,7 +273,7 @@ respite to be found with `enum`s, either. We could try define:
|
|||
enum bool { TRUE, FALSE };
|
||||
```
|
||||
|
||||
But unfortunately, all this does is define `bool` to be a numeric
|
||||
Unfortunately, all this does is define `bool` to be a numeric
|
||||
type that can hold at least 2 distinct values, and define
|
||||
numeric constants `TRUE` and `FALSE`. So in fact, you can
|
||||
_still_ write the following code:
|
||||
|
|
Loading…
Reference in New Issue
Block a user