3 ms·
I don’t buy that, the explicit version has more syntax to type but no less decisions to make. In the terse version you still need to map and select, you have to
by meheleventyone 7y ago
I don’t buy that, the explicit version has more syntax to type but no less decisions to make. In the terse version you still need to map and select, you have to make those decisions, you have to do them in the right order and you still have to get the criteria in the lambda correct.
One hides the loop (potentially doing more work because of it) and one doesn’t.
- swsieber 7y agoIn the explicit version you need to make sure you push car_id instead of i. There are more opportunities for decisions like that the longer the logic chain. The big impact as I see it is that it removes non-important bits and keeps you from messing those up, namely picking the right variable and indexing. Each new lambda in the chain is scoped to it's inputs, not all previous inputs in the iteration.
- meheleventyone 7y agoI think this falls under getting the lambda and choice of function right. Likewise more complex expressions are easy to get wrong. There are implementation details to both you need to nail. Is it better to fail in referencing the wrong variable or calling the wrong function with the wrong (but valid) input? Which is to say if you get the implementation details wrong you’ve got the implementation wrong and notice very quickly with any halfway reasonable test.
- Jtsummers 7y agoThere's no question which order to perform the map and select in the given example, there's no decision to make. You can't do the select until after the map because you're selecting based on the results of the map. If it weren't ruby, it would be obvious by the types in a language with a static type system. Of course the same is true in the for-loop example. But overall, there are more things to specify in the for-loop: let car_ids = []; for (let i = 1; i <= 5; i++) { const car_id = `car-${i}`; if (car_is_empty(car_id)) { car_ids.push(car_id); } } return car_ids; Specify: - storage location for result - loop variable - loop variable initial value - terminating condition (is it < or <=?) - increment - temp variable for producing ids - test condition for filtering - how to add new item to result return (1..5) .map{|n| "car-#{n}"} .select{|m| car_is_empty(m)} Specify: - min value of range - max value of range (are ranges inclusive or exclusive?) - how to generate id - how to query Now, this is a trivial example. I wouldn't argue that either way is clearly better than the other in this particular case. But, in my experience and opinion, the latter is much better as things become more complex.
- meheleventyone 7y agoYou certainly have to be more explicit but I don’t think most of what you actually have to specify is that important. It’s a bog standard inclusive for loop after all. That’s the decision and it’s the same as deciding I need to generate an inclusive range iterator. I will give you there is more typing to mess up and therefore if you’re not testing perhaps an issue. But we are testing the things we write? Particularly when on one side of things you put inclusive and exclusive together with the max value of the range and on the other you separate them. IME this is basically horses for courses, some people will be at home with procedural syntax and other prefer using functional concepts.
- Jtsummers 7y agoThe inclusive/exclusive question is equivalent to asking if the terminating test should be with < or <=. I paired it that way deliberately.