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.