jussi-kalliokoski / trine

A utility library for modern JavaScript.
ISC License
1.42k stars 35 forks source link

Question: Why do functions like `iterable/first` yield? #59

Open tomek-he-him opened 9 years ago

tomek-he-him commented 9 years ago

Hi,

I’ve just noticed that trine/iterable/first, trine/iterable/find and others – functions which take an iterable and spit out a single value – don’t return the resulting value but yield it.

I supposed [0, 0, 4]::find(isTruthy) would return 4 – just as [0, 0, 4].find(Boolean) returns 4 and not [4]. Out of curiosity – what’s the reason for that?

I’ve even written a library which builds upon what I supposed: iterable-some :smiley:.

jussi-kalliokoski commented 9 years ago

I admit that this is slightly weird, but it's also slightly more powerful in terms of composition... This was also one of the most difficult choices I made for the library. Say they actually returned the result instead. What would find, first or last return if there is no match, i.e. how do you detect failure? null? What if there's an actual null there? An error? Ick.

In comparison, check out this section in the README: https://github.com/jussi-kalliokoski/trine#maybe . Iterables actually implement the maybe contract (they can be mapped even if they're empty), which allows you to push error/null checks to the edges instead of doing them on every step.

I think for some() returning makes more sense instead, because there can be no failures.

tomek-he-him commented 9 years ago

What would find, first or last return if there is no match, i.e. how do you detect failure? null? What if there's an actual null there? An error? Ick.

Vanilla JS returns undefined there. I know you can have an actual undefined in an array – but then you know you’re breaking a contract.

In comparison, check out this section in the README: https://github.com/jussi-kalliokoski/trine#maybe . Iterables actually implement the maybe contract (they can be mapped even if they're empty), which allows you to push error/null checks to the edges instead of doing them on every step.

This definitely makes sense. And thanks for the link – this cleared things out a little bit. I’m quite new to this iterators thing. Now I know I have to apply .next() on the result to get the value.

I must admit I was a bit lost, I needed quick results – so I ended up with a dirty ::to(Array)[0] to get what I needed :)

Perhaps it would make sense to show .next() in the examples? Because a simple value is all that most people need:

[1,2,3]::count().next() // returns 3

– instead of:

[1,2,3]::count() // yields 3