A probably-stupid question about a Prelude implementation.

So, I'm now going over the code in the 'Report with a fine-toothed comb
because a) I'm actually able to read it now pretty fluently and b) I
want to know what's there in detail for a project I'm starting. I
stumbled across this code:
any :: (a -> Bool) -> [a] -> Bool
any p = or . map p
or :: [Bool] -> Bool
or = foldr (||) False
Now I see how this works and it's all elegant and clear and all that.
But I have two nagging problems with it (that are likely related):
1. Using foldr means I'll be traversing the whole list no matter
what. This implies (perhaps for a good reason) that it can only
work on a finite list.
2. I don't see any early bale-out semantics. The way I read this
it's going to expand a whole list of n and perform n comparisons
(including the one with the provided False).
Considering that I only need a single True result to make the whole
expression true, I'd have expected there to be some clever semantics to
allow exactly this. But what I'm seeing, unless I'm really misreading
the code, is that if I give it a list of a million boolean expressions,
it will cheerfully evaluate these million boolean expressions and
perform a million calls to (||) before giving me a result.
Please tell me I'm wrong and that I'm missing something?
--
Michael T. Richter

Hi Michael, You're wrong :)
foldr (||) False (repeat True)
Gives:
True
Remember that in Haskell everything is lazy, which means that the ||
short-circuits as soon as it can.
Thanks
Neil
On 6/22/07, Michael T. Richter
So, I'm now going over the code in the 'Report with a fine-toothed comb because a) I'm actually able to read it now pretty fluently and b) I want to know what's there in detail for a project I'm starting. I stumbled across this code:
any :: (a -> Bool) -> [a] -> Bool any p = or . map p
or :: [Bool] -> Bool or = foldr (||) False
Now I see how this works and it's all elegant and clear and all that. But I have two nagging problems with it (that are likely related):
1. Using foldr means I'll be traversing the whole list no matter what. This implies (perhaps for a good reason) that it can only work on a finite list. 2. I don't see any early bale-out semantics. The way I read this it's going to expand a whole list of n and perform n comparisons (including the one with the provided False).
Considering that I only need a single True result to make the whole expression true, I'd have expected there to be some clever semantics to allow exactly this. But what I'm seeing, unless I'm really misreading the code, is that if I give it a list of a million boolean expressions, it will cheerfully evaluate these million boolean expressions and perform a million calls to (||) before giving me a result.
Please tell me I'm wrong and that I'm missing something?
-- *Michael T. Richter*
(*GoogleTalk:* ttmrichter@gmail.com) *There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies. (Charles Hoare)* _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Neil Mitchell wrote:
Hi Michael,
You're wrong :)
foldr (||) False (repeat True)
Gives:
True
Remember that in Haskell everything is lazy, which means that the || short-circuits as soon as it can.
Thanks
Neil
Specifically it is graph reduced like this: or [F,T,F,F...] foldr (||) F [F,T,F,F...] F || foldr (||) F [T,F,F...] foldr (||) F [T,F,F...] T || foldr (||) F [F,F...] T The last line is because (T || _ = T) and lazyness

Chris Kuklewicz wrote:
Specifically it is graph reduced like this:
or [F,T,F,F...]
foldr (||) F [F,T,F,F...]
F || foldr (||) F [T,F,F...]
foldr (||) F [T,F,F...]
T || foldr (||) F [F,F...]
T
The last line is because (T || _ = T) and lazyness
I must sheepishly confess that I mistakenly throught that foldr would construct a chaint chain of ORs, which would then only be evaluated when it's returned. Now I see why there's a strict version of foldl but *not* foldr... ;-)

On Fri, Jun 22, 2007 at 11:31:17PM +0800, Michael T. Richter wrote:
1. Using foldr means I'll be traversing the whole list no matter what. This implies (perhaps for a good reason) that it can only work on a finite list.
foldr is lazy.
Please tell me I'm wrong and that I'm missing something?
You are wrong and you're missing something :) compare: any ((==) 2) [1,2,3] and any ((==) 2) [1..] any ((==) 0) [1..] will go _|_ of course. Phil -- http://www.kantaka.co.uk/ .oOo. public key: http://www.kantaka.co.uk/gpg.txt

On 22/06/07, Michael T. Richter
So, I'm now going over the code in the 'Report with a fine-toothed comb because a) I'm actually able to read it now pretty fluently and b) I want to know what's there in detail for a project I'm starting. I stumbled across this code:
any :: (a -> Bool) -> [a] -> Bool any p = or . map p
or :: [Bool] -> Bool or = foldr (||) False
Now I see how this works and it's all elegant and clear and all that. But I have two nagging problems with it (that are likely related):
Using foldr means I'll be traversing the whole list no matter what. This implies (perhaps for a good reason) that it can only work on a finite list. I don't see any early bale-out semantics. The way I read this it's going to expand a whole list of n and perform n comparisons (including the one with the provided False).
Well, try it: Prelude> any (>10) [1..] True By way of contrast, this (doesn't) work as you expected: Prelude> let any' p = foldl (||) False . map p Prelude> any' (>10) [1..] ^C Interrupted. A left fold will keep on going with an infinite list in this case. D.

Hello Michael, Friday, June 22, 2007, 7:31:17 PM, you wrote: no surprise - you got a lot of answers :) it is the best part of Haskell, after all :) the secret Haskell weapon is lazy evaluation which makes *everything* short-circuited. just consider standard (&&) definition: (&&) False _ = False (&&) True x = x this means that as far as first argument of (&&) is False, we don't even examine second one. and because everything is lazy evaluated, this second argument passed as non-evaluated *expression*. if we never examined it, it will be never evaluated: Prelude> True && (0 `div` 0>0) *** Exception: divide by zero Prelude> False && (0 `div` 0>0) False in particular, this allows to create your own control structures. and another example is that you found: infinite list may be processed as far as you may calculate result using only finite part of list: Prelude> take 10 [1..] [1,2,3,4,5,6,7,8,9,10] Prelude> and (cycle [True, False]) False in particular, last example calculated as True && False && ... where "..." remains uncalculated because we find final answer after examining second list element. i suggest you to use textual substitution to see how the last "and" call are translated into this sequence of "&&" applications. this substitution is real process of evaluating haskell code, it is called "graph reduction" by scientists :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Bulat Ziganshin wrote:
no surprise - you got a lot of answers :) it is the best part of Haskell, after all :)
Only if you ask "easy" questions. ;-)
the secret Haskell weapon is lazy evaluation which makes *everything* short-circuited. just consider standard (&&) definition:
Again, only if you Do It Right(tm). But fortunately, that's not usually difficult... :-D

On Fri, 2007-22-06 at 22:28 +0400, Bulat Ziganshin wrote:
no surprise - you got a lot of answers :) it is the best part of Haskell, after all :)
Yes, that is one of the best parts of Haskell. And I sometimes even understand the answers which is better!
the secret Haskell weapon is lazy evaluation which makes *everything* short-circuited. just consider standard (&&) definition:
(&&) False _ = False (&&) True x = x
Dammit! Teaches me to make assumptions. Of course "operators" in
Haskell are going to be JAF (Just Another Function). I didn't bother to
look one level lower than the foldr because I still have my C/C
++/practically-every-other-language-in-the-universe assumption that
"operators" are special syntactical constructs, not sugar for POBF
(Plain Old Boring Functions). Had I read the definitions of && and ||
I'd never have asked the question I did. I'd have got it right away.
(The irony is that even though the operators were being called as
functions in foldr, I have their use so ingrained in my head that I read
their use as functions in foldr and internally translated this to
imperative loops using operators!
I'd like to thank everybody for the various answers to this question.
It's been enlightening both on the front of remembering the subtleties
of Haskell's underlying assumptions and on the front of remembering what
a joy this community is.
--
Michael T. Richter

This is how I think of it: lazyIntMult :: Int -> Int -> Int lazyIntMult 0 _ = 0 lazyIntMult _ 0 = 0 lazyIntMult a b = a * b *Q> 0 * (5 `div` 0) *** Exception: divide by zero *Q> 0 `lazyIntMult` (5 `div` 0) 0 foldr evaluates a `f` (b `f` (c `f` ...)) Only f knows which arguments are strict and in which order to evaluate them. foldr knows nothing about evaluation order. Dan Michael T. Richter wrote:
So, I'm now going over the code in the 'Report with a fine-toothed comb because a) I'm actually able to read it now pretty fluently and b) I want to know what's there in detail for a project I'm starting. I stumbled across this code:
any :: (a -> Bool) -> [a] -> Bool any p = or . map p
or :: [Bool] -> Bool or = foldr (||) False
Now I see how this works and it's all elegant and clear and all that. But I have two nagging problems with it (that are likely related):
1. Using foldr means I'll be traversing the whole list no matter what. This implies (perhaps for a good reason) that it can only work on a finite list. 2. I don't see any early bale-out semantics. The way I read this it's going to expand a whole list of n and perform n comparisons (including the one with the provided False).
Considering that I only need a single True result to make the whole expression true, I'd have expected there to be some clever semantics to allow exactly this. But what I'm seeing, unless I'm really misreading the code, is that if I give it a list of a million boolean expressions, it will cheerfully evaluate these million boolean expressions and perform a million calls to (||) before giving me a result.
Please tell me I'm wrong and that I'm missing something?
-- *Michael T. Richter*
mailto:ttmrichter@gmail.com> (*GoogleTalk:* ttmrichter@gmail.com) /There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies. (Charles Hoare)/ ------------------------------------------------------------------------
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Jun 22, 2007, at 2:30 PM, Dan Weston wrote:
This is how I think of it:
lazyIntMult :: Int -> Int -> Int lazyIntMult 0 _ = 0 lazyIntMult _ 0 = 0 lazyIntMult a b = a * b
*Q> 0 * (5 `div` 0) *** Exception: divide by zero *Q> 0 `lazyIntMult` (5 `div` 0) 0
foldr evaluates a `f` (b `f` (c `f` ...))
Only f knows which arguments are strict and in which order to evaluate them. foldr knows nothing about evaluation order.
And, indeed, if you foldr a function with left zeroes, and you check for them explicitly as lazyIntMult and (||) do, then foldr is guaranteed to terminate early if it finds a zero. z is a left zero of op if for all x, z `op` x = z. This isn't the only time foldr will terminate early, but it is an important one. -Jan-Willem Maessen
Dan
participants (9)
-
Andrew Coppin
-
Bulat Ziganshin
-
Chris Kuklewicz
-
Dan Weston
-
Dougal Stanton
-
Jan-Willem Maessen
-
Michael T. Richter
-
Neil Mitchell
-
Philip Armstrong