Why "and" on an empty list returns true, does it imply that an empty list holds True? Sorry but I cannot read and comprehend this correctly, so please correct me. Thanks.
Prelude> and []
True
Prelude> or []
False
Why "and" on an empty list returns true, does it imply that an empty list holds True? Sorry but I cannot read and comprehend this correctly, so please correct me. Thanks.
Prelude> and []
True
Prelude> or []
False
In addition to @bheklilr answer, let's recall that a Monoid is a tuple
(M,e,<>)
, whereM
is a object (you can think of it as a type),e
is a point of the objectM
(e : M
- element of type) and<>
is a binary operation, which is associative and hase
as identity:There are monoid homomorphisms between some monoids. There is one free monoid - the monoid from which there is a homomorphism into any other. Such free monoid is a list:
([a], [], ++)
can be mapped into any other monoid. For example:Then
sum
,product
,and
,or
are the respective monoid homomorphisms, mapping the elements of the types[Int]
and[Bool]
. By the definition of the monoid homomorphism, the mappingh
of the monoids is performed in such a way that any listx++y
is mapped into the pointh(x ++ y) == (h x) <> (h y)
- for example,and (x++[]) == (and x) && (and [])
. It becomes clear from the latter example, that sincex++[] == x
, so(and x) && (and []) == and x
, therefore,and []
maps into the identity element of(Bool, True, &&)
.and
means: "Is everything thereTrue
?". When it's empty, everything's that is in there (which isn't much) is true, so that's a yes (True
).or
means: "Is anything thereTrue
?". When there's nothing there, there's nothing true there. (False
)In mathematics, it's often useful to talk about a binary operation, such as
&&
,||
,+
,*
, etc as having an identity. The identity is a valuee
such that the following property holds for some generic binary operation<>
For the operators I listed above, they are commutative, meaning that
x <> y = y <> x
for allx
andy
, so we only have to check one of the above properties. Forand
, the binary operator in question is&&
, and foror
the binary operator is||
. If we make a Cayley table for these operations, it would look likeSo as you can see, for
&&
if you haveTrue && False
andTrue && True
, the answer is always the second argument to&&
. For||
, if you haveFalse || False
andFalse || True
, the answer is always the second argument, so the first argument of each must be the identity element under those operators. Put simply:Thus, the preferred answer when there are no elements to perform the operator on is the identity element for each operation.
It might help to also think about the identity elements for
+
and*
, which are0
and1
respectively:You can also extend this to operations like list concatenation (
++
with[]
), function composition for functions of typea -> a
((.)
withid
), along with many others. Since this is starting to look like a pattern, you might ask if this is already a thing in Haskell, and indeed it is. The moduleData.Monoid
defines theMonoid
typeclass that abstracts this pattern, and it's minimal definition isAnd it even aliases
mappend
as<>
for ease of use (it was no accident that I choose it above for a generic binary operator). I encourage you to look at that module and play around with its definitions. The source code is quite easy to read and is enlightening.and
andor
are just folds, and a fold called on an empty list will produce its starting argument, which isTrue
orFalse
, respectively.They are implemented using a fold only if
Prelude
is loaded, otherwise they are realised using explicit recursion, which in itself still is a fold despite not actually making use offoldr
orfoldl
. They still behave the same as we can see by examining the source:Here is a link to the implementations.
To clear the confusion in the comments: A fold is a function which takes a binary function and a starting value (often called accumulator) and traverses a list until it is empty. When called on an empty list, the fold will return the accumulator as is where it does not matter if the list has already been traversed or not. This is a sample implementation of
foldr
:and
is simplywhich makes
and []
evaluate toTrue
.The logic of
and
is to find the first entry in the list which isFalse
. If the entry is not found, the result isTrue
. For example:will not iterate through the whole infinite list, but will stop at
3
and returnFalse
. There is noFalse
element in the empty list, so we default toTrue
.For
or
it's similarly: it tries to find the firstTrue
and then stops, otherwise it'sFalse
.Excellent answers, but I think it's worth providing a more intuitive treatment. Instead of
and :: [Bool] -> Bool
, however, let's look atall :: (a -> Bool) -> [Bool] -> Bool
. You can think ofall
this way. Picture the predicate (thea -> Bool
argument) as a hypothesis about list elements. Thenall
returnsFalse
if and only if the list contains at least one counterexample to the hypothesis. If the list is empty there are no counterexamples, so it's trivially confirmed.To bring it back to
and
, note thatand
andall
are interdefinable. If you haveand
, you can defineall
this way:And vice-versa, if you already had
all
, you could defineand
from it: