diff --git a/proposals/0000-deprecated-instances.rst b/proposals/0000-deprecated-instances.rst new file mode 100644 index 0000000000..0581c1742e --- /dev/null +++ b/proposals/0000-deprecated-instances.rst @@ -0,0 +1,176 @@ +Deprecated instances +==================== + +.. author:: Vladislav Zavialov +.. date-accepted:: +.. ticket-url:: https://gitlab.haskell.org/ghc/ghc/-/issues/17485 +.. implemented:: +.. highlight:: haskell +.. header:: This proposal is `discussed at this pull request `_. +.. sectnum:: +.. contents:: + +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. + +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 `_. + +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. + +Proposed Change Specification +----------------------------- + +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 + +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. + + +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. + +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. + + +Costs and Drawbacks +------------------- + +We expect the implementation and maintenance costs for this feature +to be minimal. + +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. + +Implementation Plan +------------------- + +Bartłomiej Cieślar intends to implement this as part of his internship at IOG.