4 ms·
Declarative versus imperative is sometimes in the eye of the beholder. Helping a C++ friend of mine with some Scala code the other day, there was a line like t
by dkarl 4y ago
Declarative versus imperative is sometimes in the eye of the beholder.
Helping a C++ friend of mine with some Scala code the other day, there was a line like this:
val requests = availableDrivers.filter(notPhonedHome).map(pleasePhoneHomeRequest)
When I explained filter and map to him, he thought of them as imperative. "Okay, filter loops over the array of available drivers and collects the ones that haven't phoned home in a new array. Then map loops over that array, creates a status notification for each one, and collects them in another array."
I thought of this line in a more declarative way: requests is a list of phone home requests for the drivers that haven't phoned home recently.
Both of us used a simplified mental model that didn't fully reflect what the computer is doing. My model didn't account for memory allocations. His model didn't account for potential optimizations. Neither of our models accounted for type-level trickery, possible side effects of pleasePhoneHomeRequest, or error handling (although his was closer to accounting for side effects and error handling.) Does the intermediate array actually get constructed? What happens if pleasePhoneHomeRequest throws an exception? Is requests a concrete collection type, or is it something akin to an iterator?
In practice, a language like SQL has similar concerns. Occasionally you may need to be aware that an awkwardly constructed query requires space that exceeds the size of the relevant data, or that a delete cascades in a way that is inherently slow.
tl;dr Functional programming idioms are designed to let you think more declaratively when you read and write code, but sometimes the declarative view isn't adequate for understanding the code.