Looking at the Foldable
class definition you quoted, the type of foldr
just with its arguments re-ordered a bit,
foldr' :: Foldable t => (a -> (b -> b)) -> t a -> (b -> b) ,
actually unifies with (becomes the same as) the type of
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m ,
provided b -> b
is a monoid, (b -> b) ~ Monoid m => m
i.e. Monoid (b -> b)
, the type of so-called endofunctions (i.e. functions from a type going to the same type), which are associatively combined with functional composition just as you expected.
Indeed they do form a monoid.
What does that mean, endofunctions forming a monoid under functional composition ((f . g) x = f (g x)
) ? Simply, that any two endofunctions can be composed, with functional composition being associative ( (f . g) . h == f . (g . h)
-- you can check this out yourself by using its definition). Also it means the existence of the special function id
such that id . f == f == f . id
; indeed id x = x
fits the bill. That's all, believe it or not.
Indeed (:) :: a -> [a] -> [a]
(read: (:)
has type a -> [a] -> [a]
), which is a kind of a -> b -> b
; so, with one :: Int ; one = 1
we have (one :) :: [Int] -> [Int]
which is a kind of b -> b
as well.
Also (one +) :: Int -> Int
, which is an even more specialized kind of b -> b
, but still.
As a technicality, a newtype
must be used to actually give this type its Monoid instance. This basically means, you can treat Endo
/ appEndo
as syntactic noise, when reading the code.
So foldr'
is indeed foldMap
, up to some newtype tagging (with Endo
) and untagging (with appEndo
) : appEndo (Endo f) == f
, that's all:
Sum 1 <> Sum 2 = Sum (1 + 2) -- Sum is a tag, saying "we want (+)"
Endo (1:) <> Endo (2:) = Endo ((1:) . (2:)) -- Endo is a tag, saying "we want (.)"
foldMap Sum [1,2,3] = Sum (1 + 2 + 3)
foldMap (Endo . (:)) [1,2,3] = Endo ((1:) . (2:) . (3:))
foldr' (:) [1,2,3] [4,5] = appEndo (foldMap (Endo . (:)) [1,2,3]) [4,5]
= appEndo (Endo ((1:) . (2:) . (3:))) [4,5]
= ((1:) . (2:) . (3:)) [4,5]
= [1,2,3,4,5] =
foldr (:) [4,5] [1,2,3]
Notice the lack of inner parentheses in (1 + 2 + 3)
as well as in ((1:) . (2:) . (3:))
. The associativity of <>
(here, +
, and .
) means the actual parenthesization doesn't matter semantically, and can only matter operationally. Which is the whole point, because grouping the calculations as a tree opens them up for the possibility of being calculated in parallel:
(1 + 2 + 3 + 4 + 5 + + ...)
=
( ( (1 + 2) + (3 + 4) ) + ( ( (5 + 6) + ... ) ... ) ... )
=
......
Of course the actual order with foldr
/ Endo
is linear:
foldr (:) [] [1,2,3,4]
=
1 : (2 : (3 : (4 : [] )))
(copying here some content from the comments, as we are supposed to do, on SO.)
- one trick I find helps me in understanding the tangled newtyped codes: when you see something like
Endo f <> Endo g = Endo $ x -> (f . g) x
, write it instead as an equational pseudocode, like appEndo (Endo f <> Endo g) x = (f . g) x
seen as the definition of <>
– it's not a valid Haskell, but expresses the intent more clearly, for me. I.e. I tend to see the lambdas as implementation, and equations as, well, equations (an example) ("tangled" does not apply here, of course, but e.g., like, with Continuation monad, etc.)
You're asking,
"what #.
does in (Endo #. f)
?
- well, the explanation is here. it says, in our case, that
Endo #. (:) = coerce (:) `asTypeOf` (Endo . (:))
. this is the same as writing let { g :: a -> Endo [a] ; g = coerce (:) } in g
. Endo b
is practically the same as b -> b
, save for the tag Endo
which lets us define the Monoid instance for it. instance Monoid (b->b) where ....
isn't valid Haskell. So we can read Endo #. (:)
as just Endo . (:)
.
- this has to do with what
newtype
is. it can be seen as compile-time tag. it disappears at run-time. (like, Sum 2
is actually just 2
at run time, and Product 2
is just 2
as well). But writing Sum . (1+)
hides its ephemeral nature, which apparently "can lead to very inefficient code". IOW this stuff is for compiler, it's not for the language itself. language-wise, #.
is just .
. #
is usually used in such situations.