Home > Net >  Why isn't (20 >) . length . take 10 === const True
Why isn't (20 >) . length . take 10 === const True

Time:05-08

tl;dr

Isn't the fact that 20 < length $ take 10 $ whatever requires whatever to successfully pattern patch a list (at least either [] or (_:_)) a "lack" of laziness?

Or, in other words, why isn't (20 >) . length . take 10 === const True, so that applying either of them to anything requires no evaluation of the argument whatsoever?

Is (20 >) . length . take 10 !== const True a necessity? Or a design choice? In either case, why?

Foreword

This is a follow up to my previous question.

There I asked about why fix error prints *** Exception: repeatedly and infinitely.

The answer was satisfactory.

My lucubration

However, I played around a bit with ghci and realized that take 0 $ fix error expectedly returns "", and length $ take 0 $ fix error returns 0.

On the other hand, the following prints the *** Exception: infinite flow:

20 > (length $ take 10 $ fix error)

I understand that if even one single elmenent of fix error is computed (attempted to, actually), the result is what it is, but my question is: why is the evaluation of any of them needed in the first place, in that specific expression? After all, length $ take 10 $ whatever can't be other than <= 10, hence < 20, so the expression should evaluate to True.

Actually, I see that 20 > (length $ take 10 $ [fix error]) returns immediately with True. Probably the whole point is that take 10 expects to work on a [a], and so length $ take 10 $ [fix error] doens't need to evaluate fix error to be sure it's working on a [a]. Indeed, I've verified that 20 > (length $ take 10 $ undefined) errors too (even though not with an infinitely repeating error), whereas 20 > (length $ take 10 $ [undefined]) returns with True.

Maybe that's what Willem Van Onsem meant in this comment.

Anyway, since I can write the expression above as

((20 >) . length . take 10) $ fix error

I would be tempted to say that

(20 >) . length . take 10 === const True

and hence I'd say it's reasonable for ((20 >) . length . take 10) $ fix error to return True just like const True $ fix error returns True.

But that's not the case. Why?

CodePudding user response:

take 10 has to determine if its argument is a [] or (:) value.

const True does not.


length is strict, in that it has to iterate over its entire argument. Composition doesn't mean that it only has to iterate over enough values to get a number big enough to falsify (20 >).

CodePudding user response:

After all, length $ take 10 $ whatever can't be other than <= 10 [...]

Is not correct in Haskell. The length function may in general crash or go into an infinite loop. In both cases it would not give a valid integer as result, and that lack of valid integer can not be compared to 10. In general, evaluating an arbitrary expression may result in either a value for example (1::Int) or bottom. Where bottom generalizes over all reasons why the expression may not result in a value.

The expression 20 < length $ take 10 $ whatever, therefore must evaluate to either True or bottom and for the compiler False is even an option. As you have seen both True and bottom are valid results. By contrast, const True whatever is different as it always evaluates in True without evaluating whatever.

CodePudding user response:

The behaviour you're seeing is much more to do with length than with take.

You acknowledge that length $ take 10 $ fix error is (and should be) bottom. There is not even one single element of the list fix error that can be looked at before encountering an error so there is no number that could be returned that is an accurate measure of the list's length.

However, you now want to feed that bottom value into (20 >) and expect the result True. But bottoms are interchangeable, so this would require that 20 > undefined is also True. And indeed, it would require length $ take 50 $ fix error to also be True.

Your intuition seems to be that length . take 10 is not capable of returning a value greater than 10, so (20 >) . length . take 10 should be const True, and thus shouldn't need to examine its input. You've obviously noticed that that isn't accurate for current Haskell, but I don't think it would be desirable behaviour anyway. My intuition is that (20 >) needs a specific input number that is less than 20 in order to return True; a vague notion that its input can't be a larger number isn't good enough. If its input is an error (any error) then neither True nor False is an accurate return value.

  • Related