[Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread Alfredo Di Napoli
Hi Cafe,

I was playing with the classic example of a Foldable structure: Trees.
So the code you can find both on Haskell Wiki and LYAH is the following:

data Tree a = Empty | Node (Tree a) a (Tree a) deriving (Show, Eq)

instance Foldable Tree where
foldMap f Empty = mempty
foldMap f (Node l p r) = foldMap f l `mappend` f p `mappend` foldMap f r

treeSum :: Tree Int -> Int
treeSum = Data.Foldable.foldr (+) 0


What this code does is straighforward. I was struck from the following
sentences in LYAH:

Notice that we didn't have to provide the function that takes a value and
> returns a monoid value.
> We receive that function as a parameter to foldMap and all we have to
> decide is where to apply
> that function and how to join up the resulting monoids from it.


This is obvious, so in case of (+) f = Sum, so f 3 = Sum 3 which is a
Monoid.
What I was wondering about is how Haskell knows that it has to pass, for
example, Sum in case of (+) and Product in case of (*)?
In other term, I'm missing the logical piece of the puzzle which maps the
associative binary function passed to fold up to our foldMap declaration.

Thanks for any explanation :)

Cheers,
A.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread Alfredo Di Napoli
-- Forwarded message --
From: Alfredo Di Napoli 
Date: 23 October 2012 10:35
Subject: Re: [Haskell-cafe] A clarification about what happens under the
hood with foldMap
To: Chaddaï Fouché 


I'm sure I'm missing a point, but the "minimum" definition for a Foldable
instance is given in terms of foldMap, so I get the cake for free, foldr
included, right?
In the example I have defined my treeSum as:

treeSum = Data.Foldable.foldr (+) 0

So the only thing Haskell knows it that I want to fold over a Foldable for
which foldMap (and therefore foldr) is defined, and specifically I want to
fold using (+) as function.
But foldMap is defined in terms of f, which in this case is Sum, because I
want to sum things. It it were (*) f would have been Product, right?

So what I'm missing is the relation between foldMap and foldr, aka "How
Haskell infer from (+) that I want f = Sum and not something different"?

I hope to have been clearer, don't know if I'm missing something crucial,
though :)

Thanks,
A.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread oleg

> I was playing with the classic example of a Foldable structure: Trees.
> So the code you can find both on Haskell Wiki and LYAH is the following:
>
> data Tree a = Empty | Node (Tree a) a (Tree a) deriving (Show, Eq)
>
> instance Foldable Tree where
> foldMap f Empty = mempty
> foldMap f (Node l p r) = foldMap f l `mappend` f p `mappend` foldMap f r
>
> treeSum :: Tree Int -> Int
> treeSum = Data.Foldable.foldr (+) 0
>
>
> What this code does is straighforward. I was struck from the following
> sentences in LYAH:
>
> Notice that we didn't have to provide the function that takes a value and
> > returns a monoid value.
> > We receive that function as a parameter to foldMap and all we have to
> > decide is where to apply
> > that function and how to join up the resulting monoids from it.
>
> This is obvious, so in case of (+) f = Sum, so f 3 = Sum 3 which is a
> Monoid.
> What I was wondering about is how Haskell knows that it has to pass, for
> example, Sum in case of (+) and Product in case of (*)?

Hopefully the following paradox helps:

> treeDiff :: Tree Int -> Int
> treeDiff = Data.Foldable.foldr (-) 0
>
> t1 = treeDiff (Node (Node Empty 1 Empty) 2 (Node Empty 3 Empty))

This works just as well. You might be surprised: after all, there is
no Diff monoid that corresponds to (-). In fact, there cannot be since
(-) is not associative. And yet treeDiff type checks and even produces
some integer when applied to a tree. What gives?



___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread Chaddaï Fouché
Le 23 oct. 2012 09:54, "Alfredo Di Napoli"  a
écrit :
>
> What this code does is straighforward. I was struck from the following
sentences in LYAH:
>
>> Notice that we didn't have to provide the function that takes a value
and returns a monoid value.
>> We receive that function as a parameter to foldMap and all we have to
decide is where to apply
>> that function and how to join up the resulting monoids from it.
>
>
> This is obvious, so in case of (+) f = Sum, so f 3 = Sum 3 which is a
Monoid.
> What I was wondering about is how Haskell knows that it has to pass, for
example, Sum in case of (+) and Product in case of (*)?
> In other term, I'm missing the logical piece of the puzzle which maps the
associative binary function passed to fold up to our foldMap declaration.

Haskell doesn't know it has to pass anything ! As the doc said, this is a
parameter to foldmap : so you would use "treeSum = foldmap Sum" for
example. And the binary function associed would be determined by the monoid
instance. Here that would be (+) because that's what mappend is for Sum (or
rather for Sum Int).

-- 
Jedaï
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread Chaddaï Fouché
On Tue, Oct 23, 2012 at 10:36 AM, Alfredo Di Napoli
 wrote:
> I'm sure I'm missing a point, but the "minimum" definition for a Foldable
> instance is given in terms of foldMap, so I get the cake for free, foldr
> included, right?
> In the example I have defined my treeSum as:
>
> treeSum = Data.Foldable.foldr (+) 0
>
> So the only thing Haskell knows it that I want to fold over a Foldable for
> which foldMap (and therefore foldr) is defined, and specifically I want to
> fold using (+) as function.
> But foldMap is defined in terms of f, which in this case is Sum, because I
> want to sum things. It it were (*) f would have been Product, right?
>
> So what I'm missing is the relation between foldMap and foldr, aka "How
> Haskell infer from (+) that I want f = Sum and not something different"?
>
> I hope to have been clearer, don't know if I'm missing something crucial,
> though :)

Sorry I misunderstood your first post. The answer is that Haskell
doesn't have oracular powers so it doesn't know to choose Sum if you
pass (+) and Product if you pass (*), it especially doesn't have one
kind of monoid for each possible operation you can pass to foldr... So
it proceeds otherwise. It's pretty obvious that foldr can be
implemented with foldMap, if only by using the list monoid then using
the list foldr but the actual default implementation does it more
efficiently and more directly instead, you should probably look at the
source ( 
http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.0/src/Data-Foldable.html
) then investigate the endomorphism monoid.

-- 
Jedaï

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread Alfredo Di Napoli
Thanks guys,
I'll work my way through Oleg's paradox as well as what you just said
Chaddai.
I'm very busy right now, but I'll probably come back to you tomorrow
morning, when I'll have an hour to think freely :)

Cheers,
A.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread John Lato
> From: Alfredo Di Napoli 
> Subject: [Haskell-cafe] A clarification about what happens under the
> hoodwith foldMap
>
> I'm sure I'm missing a point, but the "minimum" definition for a Foldable
> instance is given in terms of foldMap, so I get the cake for free, foldr
> included, right?
> In the example I have defined my treeSum as:
>
> treeSum = Data.Foldable.foldr (+) 0
>
> So the only thing Haskell knows it that I want to fold over a Foldable for
> which foldMap (and therefore foldr) is defined, and specifically I want to
> fold using (+) as function.
> But foldMap is defined in terms of f, which in this case is Sum, because I
> want to sum things. It it were (*) f would have been Product, right?
>
> So what I'm missing is the relation between foldMap and foldr, aka "How
> Haskell infer from (+) that I want f = Sum and not something different"?

What you're missing is that this isn't how foldr is defined.  As you
probably suspect, it isn't possible for Haskell to deduce "f = Sum"
from just the function.  And in general the function parameter to
foldr isn't even equivalent to mappend for any monoid, because it
operates on two values with different types.  Rather, foldr is defined
using the Endo monoid, which is

> newtype Endo a = Endo (a -> a)

instance Monoid (Endo a) where
mempty = id
mappend = (.)

Here's the default instance of Data.Foldable.foldr

> foldr :: (a -> b -> b) -> b -> t a -> b
> foldr f z t = appEndo (foldMap (Endo . f) t) z

What happens is that, as the tree is traversed, Leaf constructors are
replaced with 'id' (mempty :: Endo b), and branch values are replaced
with 'Endo . f', where f = (+) in this example.  Look at Endo . f:

-- Endo :: (b -> b) -> Endo b
-- f :: a -> (b -> b)
-- Endo . f :: a -> Endo b

so at each branch (Endo . f) is applied to the value, resulting in the
type 'Endo b'

foldMap just composes everything together with mappend, which, after
the Endo constructor is removed, is a giant function pipeline :: b ->
b, which is then applied to the provided base value (0 here).

John L.

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] A clarification about what happens under the hood with foldMap

2012-10-23 Thread Alfredo Di Napoli
Thanks guys for the clarification, this blowed my mind a bit :P
As far as I understood, is that my initial thought about Sum and Product
was wrong; in fact, they don't even participate to the party!
This is confirmed by the Oleg's paradox about (-).
What really happens is that Endo (which I guess is the endofunctor, so a
functor from X to X itself) act as a "semantic bridge"
between the operation to perform (+,* or whatever) and our foldable
structure.
Have I got the gist?

Thanks a lot!
A.

On 24 October 2012 02:22, John Lato  wrote:

> > From: Alfredo Di Napoli 
> > Subject: [Haskell-cafe] A clarification about what happens under the
> > hoodwith foldMap
> >
> > I'm sure I'm missing a point, but the "minimum" definition for a Foldable
> > instance is given in terms of foldMap, so I get the cake for free, foldr
> > included, right?
> > In the example I have defined my treeSum as:
> >
> > treeSum = Data.Foldable.foldr (+) 0
> >
> > So the only thing Haskell knows it that I want to fold over a Foldable
> for
> > which foldMap (and therefore foldr) is defined, and specifically I want
> to
> > fold using (+) as function.
> > But foldMap is defined in terms of f, which in this case is Sum, because
> I
> > want to sum things. It it were (*) f would have been Product, right?
> >
> > So what I'm missing is the relation between foldMap and foldr, aka "How
> > Haskell infer from (+) that I want f = Sum and not something different"?
>
> What you're missing is that this isn't how foldr is defined.  As you
> probably suspect, it isn't possible for Haskell to deduce "f = Sum"
> from just the function.  And in general the function parameter to
> foldr isn't even equivalent to mappend for any monoid, because it
> operates on two values with different types.  Rather, foldr is defined
> using the Endo monoid, which is
>
> > newtype Endo a = Endo (a -> a)
>
> instance Monoid (Endo a) where
> mempty = id
> mappend = (.)
>
> Here's the default instance of Data.Foldable.foldr
>
> > foldr :: (a -> b -> b) -> b -> t a -> b
> > foldr f z t = appEndo (foldMap (Endo . f) t) z
>
> What happens is that, as the tree is traversed, Leaf constructors are
> replaced with 'id' (mempty :: Endo b), and branch values are replaced
> with 'Endo . f', where f = (+) in this example.  Look at Endo . f:
>
> -- Endo :: (b -> b) -> Endo b
> -- f :: a -> (b -> b)
> -- Endo . f :: a -> Endo b
>
> so at each branch (Endo . f) is applied to the value, resulting in the
> type 'Endo b'
>
> foldMap just composes everything together with mappend, which, after
> the Endo constructor is removed, is a giant function pipeline :: b ->
> b, which is then applied to the provided base value (0 here).
>
> John L.
>
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe