Quantcast

ANN: generic-deepseq 1.0.0.0

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
Hello all,


Yesterday I uploaded a new package on Hackage called generic-deepseq. It
implements the 'deepseq' function generically using the new GHC.Generics
framework as found in GHC >= 7.2. It can be used as a replacement for
the deepseq package.

Given that hackage is currently down, here is an URL from a mirror where
the package description & documentation can be found:
 
http://hackage.factisresearch.com/package/generic-deepseq-1.0.0.0

Any suggestions are welcome.

Cheers,
Maxime Henrion

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Bas van Dijk-2
On 19 February 2012 13:12, Maxime Henrion <[hidden email]> wrote:
> Any suggestions are welcome.

Nice work but it would be nice to have this functionality directly in
the deepseq package as in:

#ifdef GENERICS
{-# LANGUAGE DefaultSignatures, TypeOperators, FlexibleContexts #-}
#endif

class NFData a where
    rnf :: a -> ()
    rnf a = a `seq` ()

#ifdef GENERICS
    default rnf :: (Generic a, GNFData (Rep a)) => a -> ()
    rnf = grnf . from

class GNFData f where
    grnf :: f a -> ()

instance GNFData U1 where
    grnf U1 = ()
    {-# INLINE grnf #-}

instance NFData a => GNFData (K1 i a) where
    grnf = rnf . unK1
    {-# INLINE grnf #-}

instance GNFData f => GNFData (M1 i c f) where
    grnf = grnf . unM1
    {-# INLINE grnf #-}

instance (GNFData f, GNFData g) => GNFData (f :+: g) where
    grnf (L1 x) = grnf x
    grnf (R1 x) = grnf x
    {-# INLINE grnf #-}

instance (GNFData f, GNFData g) => GNFData (f :*: g) where
    grnf (x :*: y) = grnf x `seq` grnf y
    {-# INLINE grnf #-}
#endif

Unfortunately this is not possible since the two default
implementations conflict. I see two solutions:

1) Change the DefaultSignatures extension to always give preference to
the default signature. I think giving preference to the default
signature makes sense since it's usually more specific (more
constraint) and thus "more correct" than the default implementation.

2) Remove the default implementation of rnf. I understand the default
implementation gives some convenience when writing instances for types
that have an all strict representation, as in:

instance NFData Int
instance NFData Word
instance NFData Integer
...

However, I think having the default implementation can mask some bugs as in:
data T = C Int; instance NFData T
which will neither give a compile time error nor warning.

I don't think it's that much more inconvenient to write:

instance NFData Int where rnf = rnf'
instance NFData Word where rnf = rnf'
instance NFData Integer where rnf = rnf'
...
where
rnf' :: a -> ()
rnf' a = a `seq` ()

So I would vote for option 2, removing the default rnf implementation.
If I find some time I will turn this into an official proposal.

Regards,

Bas

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
On Sun, 2012-02-19 at 16:17 +0100, Bas van Dijk wrote:

> On 19 February 2012 13:12, Maxime Henrion <[hidden email]> wrote:
> > Any suggestions are welcome.
>
> Nice work but it would be nice to have this functionality directly in
> the deepseq package as in:
>
> #ifdef GENERICS
> {-# LANGUAGE DefaultSignatures, TypeOperators, FlexibleContexts #-}
> #endif
>
> class NFData a where
>     rnf :: a -> ()
>     rnf a = a `seq` ()
>
> #ifdef GENERICS
>     default rnf :: (Generic a, GNFData (Rep a)) => a -> ()
>     rnf = grnf . from
>
> class GNFData f where
>     grnf :: f a -> ()
>
> instance GNFData U1 where
>     grnf U1 = ()
>     {-# INLINE grnf #-}
>
> instance NFData a => GNFData (K1 i a) where
>     grnf = rnf . unK1
>     {-# INLINE grnf #-}
>
> instance GNFData f => GNFData (M1 i c f) where
>     grnf = grnf . unM1
>     {-# INLINE grnf #-}
>
> instance (GNFData f, GNFData g) => GNFData (f :+: g) where
>     grnf (L1 x) = grnf x
>     grnf (R1 x) = grnf x
>     {-# INLINE grnf #-}
>
> instance (GNFData f, GNFData g) => GNFData (f :*: g) where
>     grnf (x :*: y) = grnf x `seq` grnf y
>     {-# INLINE grnf #-}
> #endif
>
> Unfortunately this is not possible since the two default
> implementations conflict. I see two solutions:
>
> 1) Change the DefaultSignatures extension to always give preference to
> the default signature. I think giving preference to the default
> signature makes sense since it's usually more specific (more
> constraint) and thus "more correct" than the default implementation.
>
> 2) Remove the default implementation of rnf. I understand the default
> implementation gives some convenience when writing instances for types
> that have an all strict representation, as in:
>
> instance NFData Int
> instance NFData Word
> instance NFData Integer
> ...
>
> However, I think having the default implementation can mask some bugs as in:
> data T = C Int; instance NFData T
> which will neither give a compile time error nor warning.
>
> I don't think it's that much more inconvenient to write:
>
> instance NFData Int where rnf = rnf'
> instance NFData Word where rnf = rnf'
> instance NFData Integer where rnf = rnf'
> ...
> where
> rnf' :: a -> ()
> rnf' a = a `seq` ()
>
> So I would vote for option 2, removing the default rnf implementation.
> If I find some time I will turn this into an official proposal.
I agree it would have been nice to have that functionality directly in
the deepseq package, or at least in a way that extends the existing
functionality rather than completely replace it. However, as you noted,
it isn't possible to do that in a backwards compatible way, unless we
hack the implementation of the DefaultSignatures extension. That being
said, even if it was possible to do this in a backwards compatible way,
I'm not entirely sure it would be desirable to do so  because there is
one subtle difference between this code and the deepseq package.

With the generic-deepseq package, you should only need to provide an
explicit DeepSeq instance for some type if it is abstract, because you
can't get a Generic instance in that case (unless the library author
derived Generic himself, but that would be a weird and dangerous thing
to do for an abstract datatype). If you're not dealing with an abstract
datatype, you _shouldn't_ have an explicit instance, because it would be
possible to write an incorrect one, while that is impossible if you just
derive a generic implementation (as long as the generic code is correct,
of course).

So, knowing that it would necessarily be backwards incompatible (I
wasn't intending to hack on GHC :-), and also that, in the end, this is
not quite the same class as the NFData class from the deepseq package, I
thought it made more sense to create another package that would be
mostly compatible with deepseq, but with a different class name so as to
force people to reevaluate the need for their instances if they have
some. I'd be interested in knowing what you and others think about that.
Maybe I'm being overly cautious?

I kept the rest of the API identical so that it's still easy to switch
to this package, thus you can still use the ($!!), force, and rnf
functions. I'm guilty of not having preserved the "rnf :: a -> ()"
function as the class function though, it's a wrapper around "deepseq"
in my code. I just didn't see the point of having a class function with
such a signature versus having a function just like "seq :: a -> b ->
b". In retrospect, that might have been a bad idea, and maybe I should
switch to have an "rnf :: a -> ()" class function to make switching even
easier?

Thanks a lot for your input!

Maxime Henrion

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Bas van Dijk-2
On 19 February 2012 18:11, Maxime Henrion <[hidden email]> wrote:
> If you're not dealing with an abstract datatype, you _shouldn't_ have an
> explicit instance, because it would be possible to write an incorrect one,
> while that is impossible if you just derive a generic implementation
> (as long as the generic code is correct, of course).

I agree. I hadn't considered this advantage yet. I guess it's the same
argument for why it's better to automatically derive Data and Typeable
instances using the DeriveDataTypeable extension.

> So, knowing that it would necessarily be backwards incompatible (I
> wasn't intending to hack on GHC :-), and also that, in the end, this is
> not quite the same class as the NFData class from the deepseq package, I
> thought it made more sense to create another package that would be
> mostly compatible with deepseq, but with a different class name so as to
> force people to reevaluate the need for their instances if they have
> some. I'd be interested in knowing what you and others think about that.
> Maybe I'm being overly cautious?

I do think it's better to integrate this into the deepseq package (and
thus removing the default implementation of rnf). Otherwise we end up
with two ways of evaluating values to normal form.

> I'm guilty of not having preserved the "rnf :: a -> ()"
> function as the class function though, it's a wrapper around "deepseq"
> in my code. I just didn't see the point of having a class function with
> such a signature versus having a function just like "seq :: a -> b ->
> b". In retrospect, that might have been a bad idea, and maybe I should
> switch to have an "rnf :: a -> ()" class function to make switching even
> easier?

I'm not sure but maybe a method like "rnf :: a -> ()" is easier to optimize.

Also in my experience (with generics support in aeson and cereal) it's
a very good idea (performance-wise) to INLINE your methods like I did
in my previous message. Of course the only way to know for sure is the
create some (criterion) benchmarks.

One last issue: Say I have a type like: "data T = C !Int"
Currently GHC Generics can't express the strictness annotation. This
means that your deepseq will unnecessarily evaluate the Int (since it
will always be evaluated already). It would be nice if the strictness
information could be added to the K1 type. (José, would it be hard to
add this to GHC.Generics?)

Regards,

Bas

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
On Sun, 2012-02-19 at 21:06 +0100, Bas van Dijk wrote:

> On 19 February 2012 18:11, Maxime Henrion <[hidden email]> wrote:
> > If you're not dealing with an abstract datatype, you _shouldn't_ have an
> > explicit instance, because it would be possible to write an incorrect one,
> > while that is impossible if you just derive a generic implementation
> > (as long as the generic code is correct, of course).
>
> I agree. I hadn't considered this advantage yet. I guess it's the same
> argument for why it's better to automatically derive Data and Typeable
> instances using the DeriveDataTypeable extension.
>
> > So, knowing that it would necessarily be backwards incompatible (I
> > wasn't intending to hack on GHC :-), and also that, in the end, this is
> > not quite the same class as the NFData class from the deepseq package, I
> > thought it made more sense to create another package that would be
> > mostly compatible with deepseq, but with a different class name so as to
> > force people to reevaluate the need for their instances if they have
> > some. I'd be interested in knowing what you and others think about that.
> > Maybe I'm being overly cautious?
>
> I do think it's better to integrate this into the deepseq package (and
> thus removing the default implementation of rnf). Otherwise we end up
> with two ways of evaluating values to normal form.
I'm okay with that, but I was interested in knowing whether you think my
reasoning for changing the class name and thus deliberately breaking the
API slightly more was sane or not (I say "more" because removing the
default implementation of rnf already constitutes an API breakage in
that the generic replacement would be optional and depends on having
Generic instances to work).

> > I'm guilty of not having preserved the "rnf :: a -> ()"
> > function as the class function though, it's a wrapper around "deepseq"
> > in my code. I just didn't see the point of having a class function with
> > such a signature versus having a function just like "seq :: a -> b ->
> > b". In retrospect, that might have been a bad idea, and maybe I should
> > switch to have an "rnf :: a -> ()" class function to make switching even
> > easier?
>
> I'm not sure but maybe a method like "rnf :: a -> ()" is easier to optimize.
>
> Also in my experience (with generics support in aeson and cereal) it's
> a very good idea (performance-wise) to INLINE your methods like I did
> in my previous message. Of course the only way to know for sure is the
> create some (criterion) benchmarks.
Yeah, I should definitely get going on the benchmarks.

> One last issue: Say I have a type like: "data T = C !Int"
> Currently GHC Generics can't express the strictness annotation. This
> means that your deepseq will unnecessarily evaluate the Int (since it
> will always be evaluated already). It would be nice if the strictness
> information could be added to the K1 type. (José, would it be hard to
> add this to GHC.Generics?)

Assuming there is way to differentiate strict constructors in
GHC.Generics, and that I have a specific instance so as to not call seq
in that case, can you actually do such an optimization safely? Consider
this code (imports omitted for simplicity):

data T = C !Int deriving Generic

instance DeepSeq T

x :: T
x = undefined

main :: IO ()
main = print (x `deepseq` ())

I would expect this to diverge, just like it does if one uses `seq`. If
we implement the optimization you suggest, I believe that deepseq
wouldn't diverge, but I admit I'm not 100% sure either.

Cheers,
Maxime Henrion

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

José Pedro Magalhães
In reply to this post by Bas van Dijk-2
Hi,

2012/2/19 Bas van Dijk <[hidden email]>

I do think it's better to integrate this into the deepseq package (and
thus removing the default implementation of rnf). Otherwise we end up
with two ways of evaluating values to normal form.

I agree with this, and I guess many people are already using the deepseq package (simply because it was there first), so it would be better to integrate the generic code with that package. As for the backwards compatibility loss, it is indeed a pity that you are forced to choose between a generic default and a "normal" default, but I don't see any easy way to change that, and the current behaviour is simple and predictable. So I do not propose we change that. In this case I agree with Bas, and propose removing the "normal" default for `rnf`.
 

One last issue: Say I have a type like: "data T = C !Int"
Currently GHC Generics can't express the strictness annotation. This
means that your deepseq will unnecessarily evaluate the Int (since it
will always be evaluated already). It would be nice if the strictness
information could be added to the K1 type. (José, would it be hard to
add this to GHC.Generics?)

I don't think so; I think the right place to put it is as a method of the Selector class, though.

But, I'm wondering, for your example, wouldn't/couldn't GHC optimize away `seq` calls to strict arguments?


Cheers,
Pedro



_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Gábor Lehel
2012/2/20 José Pedro Magalhães <[hidden email]>:

>>
>>
>> One last issue: Say I have a type like: "data T = C !Int"
>> Currently GHC Generics can't express the strictness annotation. This
>> means that your deepseq will unnecessarily evaluate the Int (since it
>> will always be evaluated already). It would be nice if the strictness
>> information could be added to the K1 type. (José, would it be hard to
>> add this to GHC.Generics?)
>
>
> I don't think so; I think the right place to put it is as a method of the
> Selector class, though.
>
> But, I'm wondering, for your example, wouldn't/couldn't GHC optimize away
> `seq` calls to strict arguments?

Isn't it also an issue that bang patterns only guarantee WHNF, but
there might be unevaluated data "further inside"? Obviously not a
problem for !Int; I don't know if the logic is there to tell the
difference.

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Herbert Valerio Riedel
In reply to this post by Bas van Dijk-2
Bas van Dijk <[hidden email]> writes:

> Also in my experience (with generics support in aeson and cereal) it's
> a very good idea (performance-wise) to INLINE your methods like I did
> in my previous message. Of course the only way to know for sure is the
> create some (criterion) benchmarks.

btw, how much is GHC able to optimize the GHC.Generics based NFData
instance as e.g. compared to what GHC is able to optimize with
deepseq-th[1]?

> One last issue: Say I have a type like: "data T = C !Int"
> Currently GHC Generics can't express the strictness annotation. This
> means that your deepseq will unnecessarily evaluate the Int (since it
> will always be evaluated already). It would be nice if the strictness
> information could be added to the K1 type. (José, would it be hard to
> add this to GHC.Generics?)

that's btw one thing that I tried hard to avoid in deepseq-th[1], by
having a hacky predicate

decWhnfIsNf :: Dec -> Q (Maybe Bool)

whis is able to detect whether WHNF is the same as NF for a given
declaration;

e.g. in the following case (see also the example in [1]):

data Foo = Foo !Int !Int !Int

data Bar = Bar !Foo !Foo

the instance generated would be:

instance NFData Foo
instance NFData Bar

since the WHNF=NF property holds for Foo as well as for Bar


 [1]: http://hackage.haskell.org/packages/archive/deepseq-th/0.1.0.2/doc/html/Control-DeepSeq-TH.html

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
In reply to this post by Bas van Dijk-2
On Sun, 2012-02-19 at 21:06 +0100, Bas van Dijk wrote:

> On 19 February 2012 18:11, Maxime Henrion <[hidden email]> wrote:
> > I'm guilty of not having preserved the "rnf :: a -> ()"
> > function as the class function though, it's a wrapper around "deepseq"
> > in my code. I just didn't see the point of having a class function with
> > such a signature versus having a function just like "seq :: a -> b ->
> > b". In retrospect, that might have been a bad idea, and maybe I should
> > switch to have an "rnf :: a -> ()" class function to make switching even
> > easier?
>
> I'm not sure but maybe a method like "rnf :: a -> ()" is easier to optimize.
>
> Also in my experience (with generics support in aeson and cereal) it's
> a very good idea (performance-wise) to INLINE your methods like I did
> in my previous message. Of course the only way to know for sure is the
> create some (criterion) benchmarks.
Well I wrote some dumb criterion benchmarks that run deepseq over
increasingly bigger lists of numbers, and it appears that using rnf as
the member function of the DeepSeq class indeed makes a _huge_
difference.

According to criterion, the performance of the old generic-deepseq code
was 6 to 7 times worse than that of the deepseq package. After switching
the class function to rnf, it got on par, if not better than the deepseq
package. I'm saying "if not", because I've observed contradicting
results from criterion, when I ran benchmarks for both packages at once,
and when I ran those separately. When running both at once,
generic-deepseq is slower than deepseq, except for the test with the
bigger list (see report-both.html). When ran separately, generic-deepseq
is consistantly faster (see report-deepseq.html and
report-gdeepseq.html). The criterion benchmark can be found on the
bitbucket repo at http://mu.org/~mux/report-deepseq.html.

I have released a 2.0.0.0 version of this package on hackage with this
change and a few more instances for base types.

Cheers,
Maxime Henrion

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

report-both.html (350K) Download Attachment
report-deepseq.html (281K) Download Attachment
report-gdeepseq.html (281K) Download Attachment
signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Johan Tibell-2
2012/2/23 Maxime Henrion <[hidden email]>:

> According to criterion, the performance of the old generic-deepseq code
> was 6 to 7 times worse than that of the deepseq package. After switching
> the class function to rnf, it got on par, if not better than the deepseq
> package. I'm saying "if not", because I've observed contradicting
> results from criterion, when I ran benchmarks for both packages at once,
> and when I ran those separately. When running both at once,
> generic-deepseq is slower than deepseq, except for the test with the
> bigger list (see report-both.html). When ran separately, generic-deepseq
> is consistantly faster (see report-deepseq.html and
> report-gdeepseq.html). The criterion benchmark can be found on the
> bitbucket repo at http://mu.org/~mux/report-deepseq.html.

When running criterion benchmarks use the -g flag to prevent
benchmarks from interfering with each other due to GC. Also, set a
high initial heap size (e.g. -H1G) so the benchmarks that run first
don't have to pay the price of GHC growing the heap.

-- Johan

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
On Thu, 2012-02-23 at 13:18 -0800, Johan Tibell wrote:

> 2012/2/23 Maxime Henrion <[hidden email]>:
> > According to criterion, the performance of the old generic-deepseq code
> > was 6 to 7 times worse than that of the deepseq package. After switching
> > the class function to rnf, it got on par, if not better than the deepseq
> > package. I'm saying "if not", because I've observed contradicting
> > results from criterion, when I ran benchmarks for both packages at once,
> > and when I ran those separately. When running both at once,
> > generic-deepseq is slower than deepseq, except for the test with the
> > bigger list (see report-both.html). When ran separately, generic-deepseq
> > is consistantly faster (see report-deepseq.html and
> > report-gdeepseq.html). The criterion benchmark can be found on the
> > bitbucket repo at http://mu.org/~mux/report-deepseq.html.
>
> When running criterion benchmarks use the -g flag to prevent
> benchmarks from interfering with each other due to GC. Also, set a
> high initial heap size (e.g. -H1G) so the benchmarks that run first
> don't have to pay the price of GHC growing the heap.
Thanks Johan! I thought the -g option was the default, so hadn't
bothered with it. Unfortunately though, it doesn't seem to make a
difference in my case, I get similar results than previously when
running both tests at once (see attached report).

Cheers,
Maxime

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

report-both2.html (283K) Download Attachment
signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Bas van Dijk-2
In reply to this post by Maxime Henrion-3
On 23 February 2012 22:09, Maxime Henrion <[hidden email]> wrote:

> On Sun, 2012-02-19 at 21:06 +0100, Bas van Dijk wrote:
>> On 19 February 2012 18:11, Maxime Henrion <[hidden email]> wrote:
>> > I'm guilty of not having preserved the "rnf :: a -> ()"
>> > function as the class function though, it's a wrapper around "deepseq"
>> > in my code. I just didn't see the point of having a class function with
>> > such a signature versus having a function just like "seq :: a -> b ->
>> > b". In retrospect, that might have been a bad idea, and maybe I should
>> > switch to have an "rnf :: a -> ()" class function to make switching even
>> > easier?
>>
>> I'm not sure but maybe a method like "rnf :: a -> ()" is easier to optimize.
>>
>> Also in my experience (with generics support in aeson and cereal) it's
>> a very good idea (performance-wise) to INLINE your methods like I did
>> in my previous message. Of course the only way to know for sure is the
>> create some (criterion) benchmarks.
>
> Well I wrote some dumb criterion benchmarks that run deepseq over
> increasingly bigger lists of numbers, and it appears that using rnf as
> the member function of the DeepSeq class indeed makes a _huge_
> difference.

Nice, that's what I expected. Have you checked if adding INLINE
pragma's helps even more? (I guess not since it's already on par with
manual written code, as you mentioned)

BTW I would also recommend making a benchmark for a big sum type.

Some nitpicking:

* In the instance:

instance GDeepSeq U1 where grnf _ = ()

I think it makes sense to pattern match on the U1 constructor, as in:
grnf U1 = ().

I haven't checked if that's necessary but my fear is that assuming:
data Unit = Unit deriving Generic; instance DeepSeq Unit
rnf (⊥ :: Unit) would equal: () while I would expect it to equal ⊥.

* Why do you have the instance:

instance GDeepSeq V1 where grnf _ = ()

The only way to construct values of a void type is using ⊥. And I
would expect that rnf ⊥ = ⊥, not (). I think the best thing is to just
remove the V1 instance.

Cheers,

Bas

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
On Thu, 2012-02-23 at 23:24 +0100, Bas van Dijk wrote:

> On 23 February 2012 22:09, Maxime Henrion <[hidden email]> wrote:
> > On Sun, 2012-02-19 at 21:06 +0100, Bas van Dijk wrote:
> >> On 19 February 2012 18:11, Maxime Henrion <[hidden email]> wrote:
> >> > I'm guilty of not having preserved the "rnf :: a -> ()"
> >> > function as the class function though, it's a wrapper around "deepseq"
> >> > in my code. I just didn't see the point of having a class function with
> >> > such a signature versus having a function just like "seq :: a -> b ->
> >> > b". In retrospect, that might have been a bad idea, and maybe I should
> >> > switch to have an "rnf :: a -> ()" class function to make switching even
> >> > easier?
> >>
> >> I'm not sure but maybe a method like "rnf :: a -> ()" is easier to optimize.
> >>
> >> Also in my experience (with generics support in aeson and cereal) it's
> >> a very good idea (performance-wise) to INLINE your methods like I did
> >> in my previous message. Of course the only way to know for sure is the
> >> create some (criterion) benchmarks.
> >
> > Well I wrote some dumb criterion benchmarks that run deepseq over
> > increasingly bigger lists of numbers, and it appears that using rnf as
> > the member function of the DeepSeq class indeed makes a _huge_
> > difference.
>
> Nice, that's what I expected. Have you checked if adding INLINE
> pragma's helps even more? (I guess not since it's already on par with
> manual written code, as you mentioned)
Oh, I had forgotten to mention that: INLINE pragmas indeed didn't make
any significant difference in my tests, so I let them out.

> BTW I would also recommend making a benchmark for a big sum type.

Yes, I should definitely do that.

> Some nitpicking:
>
> * In the instance:
>
> instance GDeepSeq U1 where grnf _ = ()
>
> I think it makes sense to pattern match on the U1 constructor, as in:
> grnf U1 = ().
>
> I haven't checked if that's necessary but my fear is that assuming:
> data Unit = Unit deriving Generic; instance DeepSeq Unit
> rnf (⊥ :: Unit) would equal: () while I would expect it to equal ⊥.
That's a good point, I will do tests to see whether this makes a
difference. This seems to mirror my thinking when I asked you whether it
would be safe to skip seq calls in the case the constructor of a type is
strict in another e-mail (have you seen it?).

> * Why do you have the instance:
>
> instance GDeepSeq V1 where grnf _ = ()
>
> The only way to construct values of a void type is using ⊥. And I
> would expect that rnf ⊥ = ⊥, not (). I think the best thing is to just
> remove the V1 instance.

This would have the consequence that any type tagged with a phantom type
(for whatever reason) couldn't be used with deepseq, it would return
bottom. What if I want to deepseq a 2-3 finger tree tagged with a
type-level natural that ensures the proper shape of the tree statically?
It seemed to me that I should be able to do that; this is why I added
this V1 instance.

Cheers,
Maxime

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
On Thu, 2012-02-23 at 23:45 +0100, Maxime Henrion wrote:

> On Thu, 2012-02-23 at 23:24 +0100, Bas van Dijk wrote:
> > * Why do you have the instance:
> >
> > instance GDeepSeq V1 where grnf _ = ()
> >
> > The only way to construct values of a void type is using ⊥. And I
> > would expect that rnf ⊥ = ⊥, not (). I think the best thing is to just
> > remove the V1 instance.
>
> This would have the consequence that any type tagged with a phantom type
> (for whatever reason) couldn't be used with deepseq, it would return
> bottom.
Sorry, I had myself confused; it wouldn't return bottom, there would
just be no way to get an instance for it (the rest of the argument still
holds).

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

José Pedro Magalhães
In reply to this post by Maxime Henrion-3
Hi,

2012/2/23 Maxime Henrion <[hidden email]>

> * Why do you have the instance:
>
> instance GDeepSeq V1 where grnf _ = ()
>
> The only way to construct values of a void type is using ⊥. And I
> would expect that rnf ⊥ = ⊥, not (). I think the best thing is to just
> remove the V1 instance.

This would have the consequence that any type tagged with a phantom type
(for whatever reason) couldn't be used with deepseq, it would return
bottom. What if I want to deepseq a 2-3 finger tree tagged with a
type-level natural that ensures the proper shape of the tree statically?
It seemed to me that I should be able to do that; this is why I added
this V1 instance.

I'm not sure I understand your comment... V1 should only be used for datatypes without constructors, such as `data Empty`.


Cheers,
Pedro


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
On Fri, 2012-02-24 at 07:49 +0100, Jos Pedro Magalhes wrote:

> Hi,
>
> 2012/2/23 Maxime Henrion <[hidden email]>
>        
>         > * Why do you have the instance:
>         >
>         > instance GDeepSeq V1 where grnf _ = ()
>         >
>         > The only way to construct values of a void type is using ⊥.
>         And I
>         > would expect that rnf ⊥ = ⊥, not (). I think the best thing
>         is to just
>         > remove the V1 instance.
>        
>        
>         This would have the consequence that any type tagged with a
>         phantom type
>         (for whatever reason) couldn't be used with deepseq, it would
>         return
>         bottom. What if I want to deepseq a 2-3 finger tree tagged
>         with a
>         type-level natural that ensures the proper shape of the tree
>         statically?
>         It seemed to me that I should be able to do that; this is why
>         I added
>         this V1 instance.
>
> I'm not sure I understand your comment... V1 should only be used for
> datatypes without constructors, such as `data Empty`.
Yes, such as the usual type-level naturals (not using DataKinds):

data Z
data S n

Those can be used to tag a type which also contains actual values that
you would want to deepseq? For example, a length-type vector? I seemed
to remember a similar construct for 2-3 finger trees that would
statically guarantee that the shape of the tree is valid, so I took that
as an example, but I don't remember the specifics.

Cheers,
Maxime
>
>


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

José Pedro Magalhães


2012/2/24 Maxime Henrion <[hidden email]>
On Fri, 2012-02-24 at 07:49 +0100, Jos Pedro Magalhes wrote:
> Hi,
>
> 2012/2/23 Maxime Henrion <[hidden email]>
>
>         > * Why do you have the instance:
>         >
>         > instance GDeepSeq V1 where grnf _ = ()
>         >
>         > The only way to construct values of a void type is using ⊥.
>         And I
>         > would expect that rnf ⊥ = ⊥, not (). I think the best thing
>         is to just
>         > remove the V1 instance.
>
>
>         This would have the consequence that any type tagged with a
>         phantom type
>         (for whatever reason) couldn't be used with deepseq, it would
>         return
>         bottom. What if I want to deepseq a 2-3 finger tree tagged
>         with a
>         type-level natural that ensures the proper shape of the tree
>         statically?
>         It seemed to me that I should be able to do that; this is why
>         I added
>         this V1 instance.
>
> I'm not sure I understand your comment... V1 should only be used for
> datatypes without constructors, such as `data Empty`.

Yes, such as the usual type-level naturals (not using DataKinds):

data Z
data S n

Those can be used to tag a type which also contains actual values that
you would want to deepseq? For example, a length-type vector?

But in those cases they are used as tags, not as values, and hence do not show up in the generic representation. So if all you want is to be able to deepseq a value of a type like

data Proxy t = Proxy

even if your value is of type `Proxy Ze`, you shouldn't need a `V1` instance.


Cheers,
Pedro


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Maxime Henrion-3
On Fri, 2012-02-24 at 09:32 +0100, Jos Pedro Magalhes wrote:

>
>
> 2012/2/24 Maxime Henrion <[hidden email]>
>         On Fri, 2012-02-24 at 07:49 +0100, Jos Pedro Magalhes wrote:
>         > Hi,
>         >
>         > 2012/2/23 Maxime Henrion <[hidden email]>
>         >
>         >         > * Why do you have the instance:
>         >         >
>         >         > instance GDeepSeq V1 where grnf _ = ()
>         >         >
>         >         > The only way to construct values of a void type is
>         using ⊥.
>         >         And I
>         >         > would expect that rnf ⊥ = ⊥, not (). I think the
>         best thing
>         >         is to just
>         >         > remove the V1 instance.
>         >
>         >
>         >         This would have the consequence that any type tagged
>         with a
>         >         phantom type
>         >         (for whatever reason) couldn't be used with deepseq,
>         it would
>         >         return
>         >         bottom. What if I want to deepseq a 2-3 finger tree
>         tagged
>         >         with a
>         >         type-level natural that ensures the proper shape of
>         the tree
>         >         statically?
>         >         It seemed to me that I should be able to do that;
>         this is why
>         >         I added
>         >         this V1 instance.
>         >
>         > I'm not sure I understand your comment... V1 should only be
>         used for
>         > datatypes without constructors, such as `data Empty`.
>        
>        
>         Yes, such as the usual type-level naturals (not using
>         DataKinds):
>        
>         data Z
>         data S n
>        
>         Those can be used to tag a type which also contains actual
>         values that
>         you would want to deepseq? For example, a length-type vector?
>
> But in those cases they are used as tags, not as values, and hence do
> not show up in the generic representation. So if all you want is to be
> able to deepseq a value of a type like
>
>         data Proxy t = Proxy
>
> even if your value is of type `Proxy Ze`, you shouldn't need a `V1`
> instance.
Oh, ok; in that case, I probably don't need that V1 instance indeed. I
should probably write QuickCheck tests using the ChasingBottoms package
in order to ensure correct behaviour of this code.

Thanks,
Maxime


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe

signature.asc (851 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

Andres Löh-2
I don't understand what's going on here. Instances for V1 should of
course be defined if they can be! And in this case, a V1 instance
makes sense and should be defined. The definition itself doesn't
matter, as it'll never be executed.

Cheers,
  Andres

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ANN: generic-deepseq 1.0.0.0

José Pedro Magalhães
Hi Andres,

2012/2/24 Andres Löh <[hidden email]>
I don't understand what's going on here. Instances for V1 should of
course be defined if they can be! And in this case, a V1 instance
makes sense and should be defined. The definition itself doesn't
matter, as it'll never be executed.

The definition certainly matters:

data Ze deriving Generic

class DeepSeq a where
  rnf :: a -> ()
  default rnf :: (Generic a, GDeepSeq (Rep a)) => a -> ()
  rnf = grnf . from

instance DeepSeq Ze

class GDeepSeq f where
  grnf :: f a -> ()

instance GDeepSeq V1 where
  grnf _ = ()

instance GDeepSeq a => GDeepSeq (M1 i c a) where
  grnf = grnf . unM1

-- other instances are not relevant now

t :: Ze
t = undefined

seq t () == undefined. rnf t == (), because the V1 instance dictates so.


Cheers,
Pedro



_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
12
Loading...