Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecated instances #575

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
176 changes: 176 additions & 0 deletions 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 <https://github.com/ghc-proposals/ghc-proposals/pull/575>`_.
.. 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 <https://hackage-search.serokell.io/?q=%5C%7B-%23+DEPRECATED>`_.

Motivation
----------

Discussions at `deepseq #16 "remove instance NFData (a -> b)" <https://github.com/haskell/deepseq/issues/16>`_
and `bytestring #140 "Surprising behavior of ByteString literals via IsString" <https://github.com/haskell/bytestring/issues/140>`_
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
<https://downloads.haskell.org/ghc/9.6.2/docs/users_guide/exts/instances.html#instance-declarations-and-resolution>`_
"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)
int-index marked this conversation as resolved.
Show resolved Hide resolved
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 <https://github.com/ghc-proposals/ghc-proposals/pull/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.