Dear Language Designers: Please copy `where` from Haskell
TLDR: where
allows to extract part of your code and give it a a new name, not like a local variable
or a private function.
I wish more languages would copy the where
construct from Haskell.
Don’t get confused by where
from SQL; it has a totally different semantics.
What is where
in Haskell
If you are not familiar with the Haskell syntax, here is a short snippet with an explanation. The following code is taken from here, with minor changes.
1: quickSort :: Ord a => [a] -> [a]
2: quickSort [] = []
3: quickSort (p:xs) = quickSort lesser ++ [p] ++ quickSort greater
4: where
5: lesser = filter (< p) xs
6: greater = filter (>= p) xs
The above function sorts a given list and returns a new list. You can read the code line by line like this:
1
Function quickSort
takes a list of type Ord
and returns a list
2 In case of an empty list, an empty list is returned
3
In case a list with items is given, we call the first element of the item p
and the rest of the elements xs
.
Then, we return a new list, which is created using three lists:
quickSort lesser
which calls our quickSort function with some elements calledlesser
- A new list containing only
p
(the first element) quickSort greater
which calls our quickSort function with some elements calledgreater
4
Well this is the gist of the whole article, our where
keywords gives the missing definitions for lesser
and greater
.
What follows are definitions of lesser
and greater
which are only valid for the quickSort
function.
5/6
lesser
is defined as the result of xs
(rest of the sorting) filtered to keep only elements which are smaller than p
.
greater
on the other hand keeps only elements larger or equal to p
If you want to understand more, read the Where!? chapter from Learn You a Haskell.
How is where
different from a private function or local variable?
where
is just syntactic sugar around a private function (imho).
You can extract a code-fragment and give it a new name.
That name is only valid within the function.
You could do the same with private functions, but then it is not guaranteed to be used solely by quikSort
.
The difference between local variables and where
is the former gives the definitions first and then usage,
while with where
the definition comes later. I found that way more readable than with local variables.
Potential where
in Java
Take this example to calculate the age. Taken from here:
public static int isAdult(LocalDate birthDate, LocalDate currentDate) {
if ((birthDate != null) && (currentDate != null)) {
return Period.between(birthDate, currentDate).getYears() >= 18;
} else {
throw new RuntimeException("wrong input");
}
}
Now, imagine if Java had a where
keyword, the above snippet could be transformed to:
public static boolean isAdult(LocalDate birthDate,
LocalDate currentDate) {
return validInput ? age >= 18 : throwError
} where validInput = (birthDate != null) && (currentDate != null)
age = Period.between(birthDate, currentDate).getYears()
throwError = throw new RuntimeException("wrong input");
Obviously, I made up the syntax.
We could argue if one is better than the other and waste our time mutually, but that is not the point.
This function now becomes easier to read. The where
syntax gives us the possibility to name each snippet,
which makes code easier to read (imho).
How about where
in JavaScript?
Let’s look at another example. This time, I was looking for a function in an actual project. One which was which I didn’t understand at the first glance. Then found the following one. I guess it is supposed to implement a (buggy) infinite scroll.
function triggerLoadMoreForPaging(container) {
const thresholdInPercent = 30;
let triggered = false;
console.log("track scrolling");
container.addEventListener("wheel", (e) => {
const howMuchScrolled =
Math.abs(container.scrollHeight - container.clientHeight - container.scrollTop) / container.scrollHeight;
if (howMuchScrolled < thresholdInPercent / 100 && !triggered) {
console.log("Trigger load more event");
triggered = true;
loadMore()
}
});
}
Image in an alternative future, we could have something like this. As said before, I would not focus on if one is better than the other. Rather, I want to point out which one is more readable.
function triggerLoadMoreForPaging(container) {
let triggered = false;
console.log("track scrolling");
container.addEventListener("wheel", (e) => {
if (hitScrollThreashold && !triggered) {
console.log("Trigger load more event");
triggered = true;
loadMore()
}
});
} where thresholdInPercent = 30
scrolledTop = Math.abs(container.scrollHeight - container.clientHeight - container.scrollTop)
howMuchScrolled = scrolledTop / container.scrollHeight
hitScrollThreashold = howMuchScrolled < thresholdInPercent / 100
Summary
Lately I have been doing more Elixir and JavaScript and sometimes,
I wished those languages would introduce the where
keyword from Haskell.
I hope you take this as food for thought.