Operators in JavaScript and Haskell
- 8 minutes
JavaScript has a handful of builtin infix operators and some convenient tricks you can do with them. Here we look at what you can do with those operators, and how you can do the same things in Haskell.
- The basic Boolean operators
&&
,||
, and!
look much the same between the two languages. - It gets more interesting when we consider what it means in JavaScript to use
&&
and||
with things that aren’t Booleans; we’ll see what operators work with theMaybe
type to us do similar things in Haskell. - JavaScript has both
if
-else
blocks and the “ternary”?
-:
operator; Haskell’sif
-then
-else
plays the role of both of these.
And and Or (Booleans)
We start off very simply: The &&
and ||
operators have the same names and the same meanings in JavaScript and Haskell.
JavaScript:
> true && false false
> true || false true
Haskell:
λ> True && False
False
λ> True || False
True
The only difference you see here is that True
and False
are capitalized in Haskell. The capital letters are important: This tells us that True
and False
are data constructors. A term that starts with a capital letters in Haskell is necessarily a data constructor. (There are other things that start with capital letters, but they are all at the type level.) While JavaScript has true
and false
as built-in keywords in the language, in Haskell they are data constructors of an ordinary type named Bool
.
The definition of the Bool
type in the standard library looks like this:
Not (Booleans)
In JavaScript, Boolean “not” is a prefix operator, !
.
> !true false
> !false true
In Haskell we have a function called not
instead of an operator. The effect is much the same, though.
λ> not False
True
λ> not True
False
Just like we saw with &&
and ||
, Haskell’s not
is an ordinary function, not a language builtin. Its definition looks something like this:
Or (Maybe)
Now we move onto stranger things: JavaScript’s ||
operator isn’t just for Booleans; you can use it with any kind of value.
||
is a builtin operator in JavaScript, but if we were to write it as a function, it would look something like this:
It returns the first argument if present (that is, truthy), and otherwise returns the second argument.
A common idiom is to apply a handful of ||
operators in sequence to select the first value that is present:
> null || null || 2 || 3 || null 2
This usage of ||
is quite similar to how we use the <|>
operator on Maybe
values in Haskell.
λ> Nothing <|> Nothing <|> Just 2 <|> Just 3 <|> Nothing
Just 2
Haskell’s closest equivalent to the concept of “truthiness” and “falsiness” is the Maybe
type. Its definition looks like this:
In JavaScript we mixed nulls and integers together directly, whereas in Haskell we’ve done something more explicit by lifting our integers into the Maybe
type to introduce the possibility of the number being absent. In the example above, each Nothing
signifies the absence of a number (places where we used null
in JavasScript), and Just 2
signifies the number 2
.
And (Maybe)
JavaScript has a similar overloading of the &&
operator. It means something like this:
The upshot is that for a JavaScript expression of the form a && b && c
, if all three values are present (that is, if they are all truthy), then the entire expression evaluates to the last of the values.
> 1 && 2 && 3 3
But if any of the values is missing (represented by a falsy value such as null
), then the evaluation terminates and the entire expression evaluates to a falsy value.
> 1 && null && 3 null
This usage of the &&
operator closely resembles the *>
operator on Maybe
values in Haskell.
λ> Just 1 *> Just 2 *> Just 3
Just 3
λ> Just 1 *> Nothing *> Just 3
Nothing
You can remember the meaning of *>
by thinking of it as an arrow pointing to the right, indicating that it discards the argument on the left and returns the argument on the right. There is a similar function <*
that does the opposite: it discards the right argument and returns the left argument.
Ternary conditional
JavaScript has two forms of conditionals. Here we show two ways to write the same code: one using an if
-else
clause, and one using a ?
-:
ternary conditional expression.
Both of these forms of conditional have a Boolean condition, a branch for the true case, and a branch for the false case. The difference is that with if
-else
, the branches are blocks, and the entire if
-else
construct is a statement; it doesn’t evaluate to a value, it only produces some effect when it runs. With the ?
-:
form, the branches are expressions rather than blocks, and the entire ?
-:
construct is also an expression; it evaluates to a value, which we can then use as the argument to the console.log
function. Each form has a benefit and a limitation.
In Haskell, we don’t have to choose. When we translate these two examples into Haskell, both of them are written using an if
-then
-else
expression.
Haskell doesn’t have the same distinction between statements and expressions that JavaScript does. In fact, Haskell doesn’t really have statements at all. Everything is an expression; everything produces some value. We’ll see often that this provides a helpful generality, such as in the above example where we could choose whether to factor out the putStrLn
function application without needing to change which syntactic construct we used.