1 Deprecated instances

GHC allows to deprecate modules, functions, data constructors, type constructors, but not instances. The lack of support for deprecation of instances makes it impossible to remove them gracefully, with an advance warning to the users. We propose to allow {-# WARNING ... #-} and {-# DEPRECATED ... #-} pragmas on instances to correct this.

1.1 Background

GHC already allows library authors to deprecate modules, functions, data constructors, and type constructors (including data types, newtypes, type synonyms, type families, data families, and classes).

Here is an example of a deprecated module:

module M {-# DEPRECATED "Do not use M" #-} where

Importing M produces the following warning:

Example.hs:3:1: warning: [-Wdeprecations]
    Module M is deprecated: "Do not use M"

And here is an example of a deprecated function:

{-# DEPRECATED f "Do not use f" #-}
f :: Int -> Int
f = id

Using f produces the following warning:

Example.hs:5:5: warning: [-Wdeprecations]
    In the use of f (imported from M): Deprecated: "Do not use f"

Library authors use deprecation warnings to great effect. Searching Hackage for {-# DEPRECATED results in 3024 matches across 656 packages.

1.2 Motivation

Discussions at deepseq #16 “remove instance NFData (a -> b)” and bytestring #140 “Surprising behavior of ByteString literals via IsString” reveal that there is demand for deprecation warnings on instances, which are currently not supported. The goal is to inform the users that the instance should be avoided before removing it outright or rendering it unusable via a TypeError constraint.

Functions, data constructors and type constructors are referenced by name in deprecation pragmas. This is not an option for instances, as instances are anonymous.

Modules use inline deprecation pragmas right before the where keyword. An attempt to add an inline pragma to an instance leads to a parse error:

-- Code:
data T
instance {-# DEPRECATED "Do not use Eq T" #-} Eq T where

-- Error message:
Example.hs:2:10: error: parse error on input {-# DEPRECATED’

We propose to allow inline deprecation pragmas on instances. The {-# WARNING ... #-} pragma is a close sibling of {-# DEPRECATED ... -}, so we propose to allow it too.

1.3 Proposed Change Specification

1.3.1 Syntax

The existing non-terminals in Parser.y are defined thus:

maybemodwarning
    : '{-# DEPRECATED' strings '#-}'
    | '{-# WARNING' strings '#-}'
    |  {- empty -}

inst_decl
    : 'instance' overlap_pragma inst_type where_inst
    | ...

stand_alone_deriving
    : 'deriving' deriv_standalone_strategy 'instance' overlap_pragma inst_type

The maybemodwarning is used in module headers. Rename it to maybewarning and employ it in inst_decl and stand_alone_deriving as follows:

inst_decl
    : 'instance' maybewarning overlap_pragma inst_type where_inst
    | ...

stand_alone_deriving
    : 'deriving' deriv_standalone_strategy 'instance' maybewarning overlap_pragma inst_type

1.3.2 Semantics

When GHC solves a constraint using an instance marked with a {-# DEPRECATED ... #-} or a {-# WARNING ... #-} pragma, it reports the attached warning.

The rules for instance matching are given in section 6.8.8 “Instance declarations and resolution” of the User’s Guide.

1.4 Examples

The notorious NFData instance can be modified as follows:

instance {-# DEPRECATED "Do not use NFData (a -> b). See deepseq issue #16" #-}
         NFData (a -> b)
  where
    rnf = rwhnf

With this change, any use of the NFData (a -> b) instance, be it explicit in user-written code or generated by Generic-based deriving, will result in a deprecation warning.

1.5 Effect and Interactions

  • We have tested and confirmed that the syntax changes do not lead to any shift/reduce or reduce/reduce conflicts. The proposed syntax is easy to parse.

  • The proposal is restricted to class instances and does not cover type family or data family instances. While it is trivial to extend the syntax, the semantics are less clear and we do not have concrete motivating examples.

1.6 Costs and Drawbacks

We expect the implementation and maintenance costs for this feature to be minimal.

1.7 Alternatives

  • An alternative, constraint-based approach, is presented in #454. The pragma-based approach proposed here is more conservative and easier to implement.

  • We could allow instance pragmas to come in arbitrary order and in postfix positions. The most general syntax would look like this:

    instance_pragmas
      : instance_pragmas warning_pragma
      | instance_pragmas overlap_pragma
      | {- empty -}
    
    inst_decl
        : 'instance' instance_pragmas inst_type instance_pragmas where_inst
        | ...
    
    stand_alone_deriving
        : 'deriving' deriv_standalone_strategy 'instance' instance_pragmas inst_type instance_pragmas
    

    We choose not to do this for the sake of simplicity and to limit the scope of the proposal, but it is conceivable that a future proposal might introduce the more general syntax.

1.8 Implementation Plan

Bartłomiej Cieślar intends to implement this as part of his internship at IOG.