I have just started learning Haskell. As I know maximum
gives the max value for a list of integers. So, maximum [2,5,7,1]
gives 7. But why by giving a tuple input does maximum always give the second element? E.g., maximum (8,1)
gives 1. The same thing happens with sum (8,1)
, product (5,2)
, minimum (4,5)
... all give the second element of the tuple. So, can anyone explain to a beginner why this happens?
Short answer: for a 2-tuple, the Foldable
instance takes (only) the second item into account. The maximum
function will thus always return the second item of the 2-tuple.
Because a 2-tuple is an instance of Foldable
. Indeed, it is defined as [src]:
instance Foldable ((,) a) where foldMap f (_, y) = f y foldr f z (_, y) = f y z
The maximum
is in essence a foldr pattern. It is implemented as an equivalent of:
maximum = foldr1 max
Where foldr1
is implemented as:
foldr1 f = fromMaybe (error "…") . foldr mf Nothing
where mf x m = Just (case m of
Nothing -> x
Just y -> f x y)
So that means that for a 2-tuple, it will be implemented as:
maximum (_, y) = fromMaybe (error "…") (mf y Nothing)
where mf x m = Just (case m of
Nothing -> x
Just y -> max x y)
So here we call mf
with y
(as x
parameter), and Nothing
as m
. The case … of …
this matches with Nothing
and returns x
. So the maximum for a 2-tuple is defined as:
maximum (_, y) = fromMaybe (error "…") (Just y)
and thus shorter:
-- maximum for a (a, b)
maximum (_, y) = y
Foldable
should be of kind * -> *
, so an instance Foldable (,)
would "crash", since that has as kind * -> * -> *
. –
Tollefson ('qwerty', 12345)
? Define the maximum of any tuple is actually senseless. But you can always define your own maxTuple
function for tuples of type (Int, Int)
–
Samford maximum ("hello", 4)
can not consider both elements. Here the type is (,) String Int
, which is seen by Foldable
as a container-like type (,) String
which happens to have Int
as its element. I'm not terribly convinced it was a good idea to allow Foldable ((,) a)
in the libraries, but if we allow that, we have no type-safe option but to consider only the second component of the pairs. –
Elephantiasis Foldable
, since it creates a lot of confusion. Yes, we can see a 2-tuple as some sort of "value with a context" (that context is then the first item), but it will likely give a lot of confusion. –
Tollefson Foldable
instance is good not because you would often want to fold a single-element container, but because it emphasizes the fact that a tuple is not just a minor variation on a list. The fact that maximum (8,1)
doesn't return 8
should force you to stop and ask yourself why you have a tuple in the first place. –
Osber maximum
is a good idea, rethink why you are using a tuple. Tuples are foldable, just not in the way lists are foldable, and that's neither good nor bad, just different. –
Osber maximum (x,y)
, a type error would be the most effective means, IMO. Unrelated: I wonder how many packages on hackage rely on such a weird instance, after it being in base
for 5? years. –
Elephantiasis maximum (x,y)
; it's to discourage you from using (x, y)
in a context where you think comparing x
and y
to each other will be necessary. –
Osber Tuples in Haskell are not multi-valued containers like they are in, say, Python. Rather, they are closer to single-valued containers like Maybe a
or Either b a
: a value with a context. While both Maybe
and Either
carry the concept of possibly no value (Either
simply providing more information than Maybe
about the lack of a value), a tuple carries the context of information about the value itself.
A value like (8, 1
) does not contain two values 8
and 1
. Rather, 8
is part of a container that contains a 1
.
As such, tuples are foldable, even if doing so seems trivial. Reducing a value of type (a, b)
to a value of b
simply has to return the value of type b
, without worrying about what to do with the other values of type b
, because there aren't any.
>>> maximum (Just 5)
5
>>> minimum (Just 5)
5
>>> maximum (Right 5)
5
>>> minimum (Right 5)
5
>>> maximum (True, 5)
5
>>> minimum (True, 5)
5
Such functions are total with tuples, though, as compared to Maybe
or Either
:
>>> maximum Nothing
*** Exception: maximum: empty structure
>>> maximum (Left 5)
*** Exception: maximum: empty structure
No matter how many types the tuple contains, all but the right-most one will fixed for any given instance of Foldable
.
-- Actual instance for (a, b)
instance Foldable ((,) a) where
foldMap f (_, y) = f y
foldr f z (_, y) = f y z
-- Instance for (a, b, c) if you wanted it
instance Foldable ((,,) a b) where
foldMap f (_, _, y) = f y
foldr f z (_, _, y) = f y z
© 2022 - 2024 — McMap. All rights reserved.
(8, 1)
can be thought of as1
with a tag of8
;(1,2,3,4,5)
can be thought of as a5
with a four distinguished tags1
,2
,3
, and4
. – Osber