Herbert Valerio Riedel | 11 Nov 15:34 2012
Picon

RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

Dear List,

After recent discussions[1][2], from which I gathered that
(compile-time) conditional APIs should be avoided, I'm left wondering if
a certain kind of compile-time conditional API falls into that category
as well, or whether it can be classified as "benign" and should
therefore be tolerated.

So I'm posting this for open discussion in the hope to reach a community
consensus on whether the conditional use of the "DefaultSignature"
language extension in APIs should be tolerated or not (e.g. for
haskell-platform packages). To this end, in the rest of this post I try
to make a case for why this class of conditional APIs is harmless.

-----

So what's bad about "conditional" APIs? The way I understand the issue
with respect to compile-time conditional APIs which either

 a) omit exposed modules or exported symbols altogether based on
    compile-time configuration, or even worse

 b) export different type-signatures for the very same symbol based on
    compile-time configuration

are "bad", as they cause problems in client code using those APIs, such
as

 a) not being able to properly declare the dependency on specific API
    features being enabled in the library API in CABAL files (since
(Continue reading)

Bas van Dijk | 11 Nov 20:22 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

On 11 November 2012 15:34, Herbert Valerio Riedel <hvr <at> gnu.org> wrote:
> I'd argue that there is no harm in allowing this kind of compile-time
> conditional APIs using the DefaultSignature language extension.

I agree.

There's one minor issue: Say some client does not intend to rely on
DefaultSignatures but forgets to write a definition for the method. He
will then not get a "No explicit method or default declaration"
warning. However, in this case I think the benefits outweigh this
disadvantage.

FWIW I prefer that deepseq and hashable both use the DefaultSignatures
extension to provide generic default definitions.

Bas
Johan Tibell | 11 Nov 20:44 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

On Sun, Nov 11, 2012 at 11:22 AM, Bas van Dijk <v.dijk.bas <at> gmail.com> wrote:

On 11 November 2012 15:34, Herbert Valerio Riedel <hvr <at> gnu.org> wrote:
> I'd argue that there is no harm in allowing this kind of compile-time
> conditional APIs using the DefaultSignature language extension.

I agree.

There's one minor issue: Say some client does not intend to rely on
DefaultSignatures but forgets to write a definition for the method. He
will then not get a "No explicit method or default declaration"
warning. However, in this case I think the benefits outweigh this
disadvantage.

FWIW I prefer that deepseq and hashable both use the DefaultSignatures
extension to provide generic default definitions.

hashable won't be using DefaultSignatures. See the discussion here:


_______________________________________________
Libraries mailing list
Libraries <at> haskell.org
http://www.haskell.org/mailman/listinfo/libraries
Herbert Valerio Riedel | 14 Dec 11:13 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

Johan Tibell <johan.tibell <at> gmail.com> writes:
> On Sun, Nov 11, 2012 at 11:22 AM, Bas van Dijk <v.dijk.bas <at> gmail.com> wrote:
>> On 11 November 2012 15:34, Herbert Valerio Riedel <hvr <at> gnu.org> wrote:
>>> I'd argue that there is no harm in allowing this kind of compile-time
>>> conditional APIs using the DefaultSignature language extension.
>>
>> I agree.
>>
>> There's one minor issue: Say some client does not intend to rely on
>> DefaultSignatures but forgets to write a definition for the method. He
>> will then not get a "No explicit method or default declaration"
>> warning. However, in this case I think the benefits outweigh this
>> disadvantage.
>>
>> FWIW I prefer that deepseq and hashable both use the DefaultSignatures
>> extension to provide generic default definitions.
>
> hashable won't be using DefaultSignatures. See the discussion here:
>
> https://github.com/tibbe/hashable/pull/33

Update: However, starting with hashable-1.2.0.0 DefaultSignatures are
        being used notwithstanding :-)

cheers,
hvr
Ian Lynagh | 11 Nov 20:59 2012

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

On Sun, Nov 11, 2012 at 08:22:08PM +0100, Bas van Dijk wrote:
> On 11 November 2012 15:34, Herbert Valerio Riedel <hvr <at> gnu.org> wrote:
> > I'd argue that there is no harm in allowing this kind of compile-time
> > conditional APIs using the DefaultSignature language extension.
> 
> I agree.
> 
> There's one minor issue: Say some client does not intend to rely on
> DefaultSignatures but forgets to write a definition for the method. He
> will then not get a "No explicit method or default declaration"
> warning. However, in this case I think the benefits outweigh this
> disadvantage.

If deepseq does not use DefaultSignatures, then clients must write

    instance NFData MyType where
        rnf = ... e ...

If deepseq conditionally uses DefaultSignatures, and a client wishes to
make use of that but still remain portable, then it must write

    instance NFData MyType where
    #if COMPILER_SUPPORTS_DEFAULT_SIGNATURES
        rnf = ... e ...
    #endif

which, even ignoring the fact that we don't have an easy way to get a
correct COMPILER_SUPPORTS_DEFAULT_SIGNATURES symbol at the moment, is
more typing, and would not result in better/faster code.

And worse still, if someone with a featureful compiler forgets to write
the instance body, they will get no warning that it will not work for
other users.

It's true that if the client's portability is <= that of
DefaultSignatures for other reasons then it could omit the instance
body, but that feels like a dangerous direction to head in to me. And
it would not be much more effort to write

    instance NFData MyType where
        rnf = genericRnf

(where genericRnf is exported by the deepseq package).

I think deepseq should either unconditionally use DefaultSignatures, or
not use them at all. At this point, my preference would be the latter.

Thanks
Ian
Herbert Valerio Riedel | 11 Nov 21:31 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

Bas van Dijk <v.dijk.bas <at> gmail.com> writes:

[...]

> There's one minor issue: Say some client does not intend to rely on
> DefaultSignatures but forgets to write a definition for the method. He
> will then not get a "No explicit method or default declaration"
> warning. However, in this case I think the benefits outweigh this
> disadvantage.

...maybe the following shameless plug offers a different perspective
in the design-space of DefaultSignatures:

 http://hackage.haskell.org/trac/ghc/ticket/7395#comment:7

But I'm not sure yet, whether handling DefaultSignatures implementations
more like builtin classes and keeping vanilla H98 typeclass
default-implementations in separate "namespace" would actually help
here...
Henning Thielemann | 11 Nov 21:21 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?


On Sun, 11 Nov 2012, Herbert Valerio Riedel wrote:

> E.g. consider the following hypothetical class definition:
>
> 	class Size a where
> 	  size :: a -> Int
> 	#ifdef HAS_DEFAULT_SIGNATURE
> 	  default size :: Generic a => a -> Int
>      size x = ...Generics based implementation...
> 	#endif
>
> Now, if the client code doesn't exploit the default-signature
> implementation, then the client code is perfectly portable, and even if
> the conditional API features are available they have no visible effect
> whatsoever.
>
> OTOH, if the client code wants to make use of the default
> implementation, it needs to be built with a compiler supporting the
> Generics language feature, and due to a) the library is guaranteed to
> provide the default-signature implementation as well as soon as the
> client is able to use it. The client-code is now deliberately
> non-portable (but on the other hand, the operational semantics are
> guaranteed to be consistent, as there's only one case to consider)

That is, if I want to write a package that is portable I have to implement 
'size' myself. But if I forget to do so then GHC will not warn me. That 
is, I have to know and remember that 'size' is somehow special.

APIs that change according to available compiler features are more 
problematic than they look first. E.g. QuickCheck depends on 
TemplateHaskell if you run on GHC. That is, if you run on GHC then a 
module is included that provides TemplateHaskell functions. If you do not 
run on GHC this module is not included because you cannot use it anyway. 
However, this way QuickCheck becomes restricted to certain _versions_ of 
GHC, because TemplateHaskell changes from version to version of GHC. Thus 
when speaking about portability, we should always think about portability 
between GHC versions. (My proposal for a quote of the week is: GHC's 
strongest competitor is the next version of GHC.) Will the Generics based 
default implementation of 'size' be portable to future version of 
Generics?

For a central package like deepseq I would prefer to provide functions 
like genericSize that other libraries can use if they want to rely on 
Generics. They would then write

   instance Size Foo where
      size = genericSize

instead of

   instance Size Foo where

This would be ok, wouldn't it?
Herbert Valerio Riedel | 11 Nov 22:06 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

Henning Thielemann <lemming <at> henning-thielemann.de> writes:
> On Sun, 11 Nov 2012, Herbert Valerio Riedel wrote:
>> E.g. consider the following hypothetical class definition:
>>
>> 	class Size a where
>> 	  size :: a -> Int
>> 	#ifdef HAS_DEFAULT_SIGNATURE
>> 	  default size :: Generic a => a -> Int
>>      size x = ...Generics based implementation...
>> 	#endif
>>
>> Now, if the client code doesn't exploit the default-signature
>> implementation, then the client code is perfectly portable, and even if
>> the conditional API features are available they have no visible effect
>> whatsoever.
>>
>> OTOH, if the client code wants to make use of the default
>> implementation, it needs to be built with a compiler supporting the
>> Generics language feature, and due to a) the library is guaranteed to
>> provide the default-signature implementation as well as soon as the
>> client is able to use it. The client-code is now deliberately
>> non-portable (but on the other hand, the operational semantics are
>> guaranteed to be consistent, as there's only one case to consider)
>
> That is, if I want to write a package that is portable I have to
> implement 'size' myself. But if I forget to do so then GHC will not
> warn me. That is, I have to know and remember that 'size' is somehow
> special.

 a) If you are allowed to forget to implement 'size', then your type was
    able to provide a 'Generic' instance, how was that instance possible
    to come into existence in the first place?

 b) what if GHC provided something akin to a
    -fwarn-instantiate-default-signatures GHC option, which might even
    be turned on by default?

 c) what if GHC behaved like outlined in
    http://hackage.haskell.org/trac/ghc/ticket/7395#comment:7 instead?

> APIs that change according to available compiler features are more
> problematic than they look first. E.g. QuickCheck depends on
> TemplateHaskell if you run on GHC. That is, if you run on GHC then a
> module is included that provides TemplateHaskell functions. If you do
> not run on GHC this module is not included because you cannot use it
> anyway. However, this way QuickCheck becomes restricted to certain
> _versions_ of GHC, because TemplateHaskell changes from version to
> version of GHC. Thus when speaking about portability, we should always
> think about portability between GHC versions. (My proposal for a quote
> of the week is: GHC's strongest competitor is the next version of
> GHC.) Will the Generics based default implementation of 'size' be
> portable to future version of Generics?

Fair enough, but breakage with newer GHCs can also happen when staying
in the pure Haskell98/2010 domain (just think of the Num/Show/Eq
superclass decoupling that occured in recent GHC versions). That is, for
Haskell libraries there's always the risk needing maintainance when a
new GHC version comes out. (But I'm not arguing this to be a good thing
either...)

> For a central package like deepseq I would prefer to provide functions
> like genericSize that other libraries can use if they want to rely on
> Generics. They would then write
>
>   instance Size Foo where
>      size = genericSize
>
> instead of
>
>   instance Size Foo where
>
>
> This would be ok, wouldn't it?

Are you preferring 'genericSize' to avoid the implicity that comes with
DefaultSignatures, and the resulting risk of inadvertently writing
non-portable code because GHC currently doesn't warn you?
José Pedro Magalhães | 12 Nov 10:27 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

Hi,

I would prefer it if packages would use DefaultSignatures conditionally, instead of creating new packages just to define a generic variant of a function. In general, though, I think it's good practice to define the defaults as separate functions (like `genericSize`) and export them, so that those not using DefaultSignatures can still write an instance easily.

As for the concerns about people using DefaultSignatures and not realizing their code is not portable, I guess we could reject programs that use a default signature (when writing an empty instance) unless they enable -XDefaultSignatures. Then people would know they are relying on an extension.


Cheers,
Pedro



On Sun, Nov 11, 2012 at 9:06 PM, Herbert Valerio Riedel <hvr <at> gnu.org> wrote:
Henning Thielemann <lemming <at> henning-thielemann.de> writes:
> On Sun, 11 Nov 2012, Herbert Valerio Riedel wrote:
>> E.g. consider the following hypothetical class definition:
>>
>>      class Size a where
>>        size :: a -> Int
>>      #ifdef HAS_DEFAULT_SIGNATURE
>>        default size :: Generic a => a -> Int
>>      size x = ...Generics based implementation...
>>      #endif
>>
>> Now, if the client code doesn't exploit the default-signature
>> implementation, then the client code is perfectly portable, and even if
>> the conditional API features are available they have no visible effect
>> whatsoever.
>>
>> OTOH, if the client code wants to make use of the default
>> implementation, it needs to be built with a compiler supporting the
>> Generics language feature, and due to a) the library is guaranteed to
>> provide the default-signature implementation as well as soon as the
>> client is able to use it. The client-code is now deliberately
>> non-portable (but on the other hand, the operational semantics are
>> guaranteed to be consistent, as there's only one case to consider)
>
> That is, if I want to write a package that is portable I have to
> implement 'size' myself. But if I forget to do so then GHC will not
> warn me. That is, I have to know and remember that 'size' is somehow
> special.

 a) If you are allowed to forget to implement 'size', then your type was
    able to provide a 'Generic' instance, how was that instance possible
    to come into existence in the first place?

 b) what if GHC provided something akin to a
    -fwarn-instantiate-default-signatures GHC option, which might even
    be turned on by default?

 c) what if GHC behaved like outlined in
    http://hackage.haskell.org/trac/ghc/ticket/7395#comment:7 instead?

> APIs that change according to available compiler features are more
> problematic than they look first. E.g. QuickCheck depends on
> TemplateHaskell if you run on GHC. That is, if you run on GHC then a
> module is included that provides TemplateHaskell functions. If you do
> not run on GHC this module is not included because you cannot use it
> anyway. However, this way QuickCheck becomes restricted to certain
> _versions_ of GHC, because TemplateHaskell changes from version to
> version of GHC. Thus when speaking about portability, we should always
> think about portability between GHC versions. (My proposal for a quote
> of the week is: GHC's strongest competitor is the next version of
> GHC.) Will the Generics based default implementation of 'size' be
> portable to future version of Generics?

Fair enough, but breakage with newer GHCs can also happen when staying
in the pure Haskell98/2010 domain (just think of the Num/Show/Eq
superclass decoupling that occured in recent GHC versions). That is, for
Haskell libraries there's always the risk needing maintainance when a
new GHC version comes out. (But I'm not arguing this to be a good thing
either...)

> For a central package like deepseq I would prefer to provide functions
> like genericSize that other libraries can use if they want to rely on
> Generics. They would then write
>
>   instance Size Foo where
>      size = genericSize
>
> instead of
>
>   instance Size Foo where
>
>
> This would be ok, wouldn't it?

Are you preferring 'genericSize' to avoid the implicity that comes with
DefaultSignatures, and the resulting risk of inadvertently writing
non-portable code because GHC currently doesn't warn you?


_______________________________________________
Libraries mailing list
Libraries <at> haskell.org
http://www.haskell.org/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
Libraries <at> haskell.org
http://www.haskell.org/mailman/listinfo/libraries
Roman Cheplyaka | 12 Nov 10:44 2012

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

* José Pedro Magalhães <jpm <at> cs.uu.nl> [2012-11-12 09:27:17+0000]
> Hi,
> 
> I would prefer it if packages would use DefaultSignatures conditionally,
> instead of creating new packages just to define a generic variant of a
> function. In general, though, I think it's good practice to define the
> defaults as separate functions (like `genericSize`) and export them, so
> that those not using DefaultSignatures can still write an instance easily.

Do you propose exposing genericSize conditionally?

I'm strongly against this. I think that packages' interfaces may not
depend on the compiler or compilation options. Only the implementation
of the intreface may.

> As for the concerns about people using DefaultSignatures and not realizing
> their code is not portable, I guess we could reject programs that use a
> default signature (when writing an empty instance) unless they enable
> -XDefaultSignatures. Then people would know they are relying on an
> extension.

Can't we simply ignore default signatures (and corresponding default
definitions) when DefaultSignatures is off?

E.g.

  instance Show Foo

is accepted in any case, but has different semantics depending on
whether DefaultSignatures is on. This way it's more Haskell2010
compatible.

I realise that changing semantics is not very good, but people usually
test this kind of code anyway, so I don't think it will be a big
problem in practice.

In that case it'd make sense to add a separate flag
(-XUseGenericDefinition?) whose impact on the program semantics is more
obvious from its name.

Roman
José Pedro Magalhães | 12 Nov 12:36 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

Hi Roman,


On Mon, Nov 12, 2012 at 9:44 AM, Roman Cheplyaka <roma <at> ro-che.info> wrote:
* José Pedro Magalhães <jpm <at> cs.uu.nl> [2012-11-12 09:27:17+0000]
> Hi,
>
> I would prefer it if packages would use DefaultSignatures conditionally,
> instead of creating new packages just to define a generic variant of a
> function. In general, though, I think it's good practice to define the
> defaults as separate functions (like `genericSize`) and export them, so
> that those not using DefaultSignatures can still write an instance easily.

Do you propose exposing genericSize conditionally?

No; it should be exported in both cases (it doesn't harm exporting it even when DefaultSignatures is supported).


Cheers,
Pedro


_______________________________________________
Libraries mailing list
Libraries <at> haskell.org
http://www.haskell.org/mailman/listinfo/libraries
Bas van Dijk | 12 Nov 10:45 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?

I like those ideas.

However this still leaves #7395 unresolved.

I do like hvr's idea of using the deriving mechanism to derive classes
with DefaultSignatures.

Bas

#7395 http://hackage.haskell.org/trac/ghc/ticket/7395

On 12 November 2012 10:27, José Pedro Magalhães <jpm <at> cs.uu.nl> wrote:
> Hi,
>
> I would prefer it if packages would use DefaultSignatures conditionally,
> instead of creating new packages just to define a generic variant of a
> function. In general, though, I think it's good practice to define the
> defaults as separate functions (like `genericSize`) and export them, so that
> those not using DefaultSignatures can still write an instance easily.
>
> As for the concerns about people using DefaultSignatures and not realizing
> their code is not portable, I guess we could reject programs that use a
> default signature (when writing an empty instance) unless they enable
> -XDefaultSignatures. Then people would know they are relying on an
> extension.
>
>
> Cheers,
> Pedro
>
>
>
> On Sun, Nov 11, 2012 at 9:06 PM, Herbert Valerio Riedel <hvr <at> gnu.org> wrote:
>>
>> Henning Thielemann <lemming <at> henning-thielemann.de> writes:
>> > On Sun, 11 Nov 2012, Herbert Valerio Riedel wrote:
>> >> E.g. consider the following hypothetical class definition:
>> >>
>> >>      class Size a where
>> >>        size :: a -> Int
>> >>      #ifdef HAS_DEFAULT_SIGNATURE
>> >>        default size :: Generic a => a -> Int
>> >>      size x = ...Generics based implementation...
>> >>      #endif
>> >>
>> >> Now, if the client code doesn't exploit the default-signature
>> >> implementation, then the client code is perfectly portable, and even if
>> >> the conditional API features are available they have no visible effect
>> >> whatsoever.
>> >>
>> >> OTOH, if the client code wants to make use of the default
>> >> implementation, it needs to be built with a compiler supporting the
>> >> Generics language feature, and due to a) the library is guaranteed to
>> >> provide the default-signature implementation as well as soon as the
>> >> client is able to use it. The client-code is now deliberately
>> >> non-portable (but on the other hand, the operational semantics are
>> >> guaranteed to be consistent, as there's only one case to consider)
>> >
>> > That is, if I want to write a package that is portable I have to
>> > implement 'size' myself. But if I forget to do so then GHC will not
>> > warn me. That is, I have to know and remember that 'size' is somehow
>> > special.
>>
>>  a) If you are allowed to forget to implement 'size', then your type was
>>     able to provide a 'Generic' instance, how was that instance possible
>>     to come into existence in the first place?
>>
>>  b) what if GHC provided something akin to a
>>     -fwarn-instantiate-default-signatures GHC option, which might even
>>     be turned on by default?
>>
>>  c) what if GHC behaved like outlined in
>>     http://hackage.haskell.org/trac/ghc/ticket/7395#comment:7 instead?
>>
>> > APIs that change according to available compiler features are more
>> > problematic than they look first. E.g. QuickCheck depends on
>> > TemplateHaskell if you run on GHC. That is, if you run on GHC then a
>> > module is included that provides TemplateHaskell functions. If you do
>> > not run on GHC this module is not included because you cannot use it
>> > anyway. However, this way QuickCheck becomes restricted to certain
>> > _versions_ of GHC, because TemplateHaskell changes from version to
>> > version of GHC. Thus when speaking about portability, we should always
>> > think about portability between GHC versions. (My proposal for a quote
>> > of the week is: GHC's strongest competitor is the next version of
>> > GHC.) Will the Generics based default implementation of 'size' be
>> > portable to future version of Generics?
>>
>> Fair enough, but breakage with newer GHCs can also happen when staying
>> in the pure Haskell98/2010 domain (just think of the Num/Show/Eq
>> superclass decoupling that occured in recent GHC versions). That is, for
>> Haskell libraries there's always the risk needing maintainance when a
>> new GHC version comes out. (But I'm not arguing this to be a good thing
>> either...)
>>
>> > For a central package like deepseq I would prefer to provide functions
>> > like genericSize that other libraries can use if they want to rely on
>> > Generics. They would then write
>> >
>> >   instance Size Foo where
>> >      size = genericSize
>> >
>> > instead of
>> >
>> >   instance Size Foo where
>> >
>> >
>> > This would be ok, wouldn't it?
>>
>> Are you preferring 'genericSize' to avoid the implicity that comes with
>> DefaultSignatures, and the resulting risk of inadvertently writing
>> non-portable code because GHC currently doesn't warn you?
>>
>>
>> _______________________________________________
>> Libraries mailing list
>> Libraries <at> haskell.org
>> http://www.haskell.org/mailman/listinfo/libraries
>
>
>
> _______________________________________________
> Libraries mailing list
> Libraries <at> haskell.org
> http://www.haskell.org/mailman/listinfo/libraries
>

_______________________________________________
Libraries mailing list
Libraries <at> haskell.org
http://www.haskell.org/mailman/listinfo/libraries
Henning Thielemann | 12 Nov 10:58 2012
Picon

Re: RFC: Can DefaultSignature compile-time conditional APIs be regarded "benign"?


On Mon, 12 Nov 2012, Bas van Dijk wrote:

> I like those ideas.
>
> However this still leaves #7395 unresolved.
>
> I do like hvr's idea of using the deriving mechanism to derive classes
> with DefaultSignatures.

The more I think about it the more my impression becomes that we are 
trying to solve a problem the complicated way where a simple solution 
already exists. I see the motivation behind DefaultSignatures and 
encountered the situation often enough that lead to the introduction of 
DefaultSignatures. However I always solved the problem by providing 
functions like genericSize or genericRnf (though I have not used Generics 
for default implementations). Its simple and its portable. Now with 
DefaultSignatures we can simplify some bits, but in order to stay portable 
with DefaultSignatures things get more complicated than staying portable 
without DefaultSignatures at all.

Gmane