David Feuer
2018-02-14 04:06:48 UTC
The time has come to remove some deprecated functions from containers.
As many know, lots of people ignore deprecation warnings. That means
there are probably plenty of people still using the deprecated
functions.
Right now, the functions work, and give deprecation warnings telling
users what alternatives to use. If I simply remove the functions,
users will suddenly have to *actually* do something to fix their code,
but they *won't* be given any useful information about how to do it.
Ideally, GHC would add a REMOVED pragma, but I don't see any sign that
that's happening.
Pondering that conundrum today, I came up with something that seems to
*mostly* solve the problem:
-- We need this class to defer the type error to the call site, rather
-- than getting it at the definition site. I think it basically takes advantage
-- of the delayed resolution GHC uses to support overlapping instances,
-- which is kind of gross.
class Error (e :: ErrorMessage)
instance TypeError e => Error e
insertWith' :: Error ('Text "Data.IntMap.insertWith' is gone. Use
Data.IntMap.Strict.insertWith.")
=> (a -> a -> a) -> Key -> a -> IntMap a -> IntMap a
insertWith' _ _ _ _ = undefined
Now if someone is still using Data.IntMap.insertWith', they'll get an
error message, but a useful one. Great! But there are two downsides:
1. An unqualified, unrestricted import of the module will still use up
a perfectly good name for as long as the export is still there.
2. User-defined type errors have only existed since GHC 8.0. So for
earlier versions, I'd probably need to just remove the functions, in
which case the exports depend on the GHC version, which is
unfortunate.
In these *particular* cases, I suspect the benefits of this hack
outweigh the downsides, because unqualified and unrestricted imports
of Data.Map and Data.IntMap are fairly rare.
What are others' thoughts on the matter?
David
As many know, lots of people ignore deprecation warnings. That means
there are probably plenty of people still using the deprecated
functions.
Right now, the functions work, and give deprecation warnings telling
users what alternatives to use. If I simply remove the functions,
users will suddenly have to *actually* do something to fix their code,
but they *won't* be given any useful information about how to do it.
Ideally, GHC would add a REMOVED pragma, but I don't see any sign that
that's happening.
Pondering that conundrum today, I came up with something that seems to
*mostly* solve the problem:
-- We need this class to defer the type error to the call site, rather
-- than getting it at the definition site. I think it basically takes advantage
-- of the delayed resolution GHC uses to support overlapping instances,
-- which is kind of gross.
class Error (e :: ErrorMessage)
instance TypeError e => Error e
insertWith' :: Error ('Text "Data.IntMap.insertWith' is gone. Use
Data.IntMap.Strict.insertWith.")
=> (a -> a -> a) -> Key -> a -> IntMap a -> IntMap a
insertWith' _ _ _ _ = undefined
Now if someone is still using Data.IntMap.insertWith', they'll get an
error message, but a useful one. Great! But there are two downsides:
1. An unqualified, unrestricted import of the module will still use up
a perfectly good name for as long as the export is still there.
2. User-defined type errors have only existed since GHC 8.0. So for
earlier versions, I'd probably need to just remove the functions, in
which case the exports depend on the GHC version, which is
unfortunate.
In these *particular* cases, I suspect the benefits of this hack
outweigh the downsides, because unqualified and unrestricted imports
of Data.Map and Data.IntMap are fairly rare.
What are others' thoughts on the matter?
David