Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deprecate a particular type-class instance?

I'm trying to do the following, but constantly running into parse errors while compiling this source-code:

instance FromRow Job where
  {-# DEPRECATED fromRow "Please do not depend on the FromRow instance of the Job type. It does not handle optional features, such as job-results and job-workflows. Depending upon your scenario, use 'jobRowParserSimple' or 'jobRowParserWithWorkflow' directly." #-}
  fromRow = jobRowParserSimple

Is it possible in Haskell to do this? How?

like image 751
Saurabh Nanda Avatar asked Oct 29 '25 01:10

Saurabh Nanda


1 Answers

I am unable to realize what you seek.

I can only suggest a variant: we can make GHC raise an error when the instance is used, unless the user of the instance confirms that they really want to enable the deprecated instance.

This is not an ideal solution, but I could not find anything closer to the goal.

Anyway, here's how it is done. We first define, in the library, some boilerplate class and instances:

{-# LANGUAGE DataKinds, TypeFamilies, UndecidableInstances #-}

import GHC.TypeError
   
class MakeError a

instance
   (TypeError (Text "Please do not depend on the FromRow instance of the Job type. It does not handle optional features, such as job-results and job-workflows. Depending upon your scenario, use 'jobRowParserSimple' or 'jobRowParserWithWorkflow' directly."))
   => MakeError Job

Add here further instances as needed, one for each deprecation you need.

Then, still in the library code, we add a class with an overlappable instance, referring to MakeError which will generate the error message.

class EnableDeprecated a

instance {-# OVERLAPPABLE #-} MakeError a => EnableDeprecated a

Finally, still in the library code, we add the instance we want to deprecate. Here I defined a dummy class, type, and instance.

class FromRow a where
   fromRow :: a

data Job

-- Note the deprecation constraint.
instance EnableDeprecated Job => FromRow Job where
  fromRow = undefined

Finally, after having completed the library code, we consider the user code. Here's a dummy test using the deprecated instance.

test :: Job
test = fromRow

This user code will trigger an error, showing the error message mentioning the deprecation.

If the user wants to disable the deprecation, they can add the following instance:

instance EnableDeprecated Job

No more errors are now raised.

This solution is not ideal for many reasons. The main issue here is that we rely on overlapping instances, so it is crucial that the last instance EnableDeprecated Job is in scope everywhere, otherwise we might getting the deprecation error, even when the instance is present elsewhere. Note that this is an orphan instance, which makes the overlapping machinery fragile: the burden is on the programmer to keep this instance on scope everywhere.

like image 93
chi Avatar answered Oct 31 '25 17:10

chi