GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2024-01-16T15:13:54Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/24323Non-standalone deriving clause does not pick up superclass quantified coercib...2024-01-16T15:13:54ZShea Levyshea@shealevy.comNon-standalone deriving clause does not pick up superclass quantified coercible constraint## Summary
I have a case where a standalone deriving via typechecks but the non-standalone equivalent does not due to unknown roles, despite the derived class constraining the roles appropriately:
```haskell
{-# LANGUAGE DerivingVia #-...## Summary
I have a case where a standalone deriving via typechecks but the non-standalone equivalent does not due to unknown roles, despite the derived class constraining the roles appropriately:
```haskell
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE QuantifiedConstraints #-}
module Test where
import Control.Exception
import Data.Coerce
import Control.Monad.Trans.Reader
class (Monad m, forall x y. Coercible x y => Coercible (m x) (m y)) => MonadWith m where
stateThreadingGeneralWith
:: GeneralAllocate m SomeException releaseReturn b a
-> (a -> m b)
-> m (b, releaseReturn)
{-
newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad) deriving MonadWith via ReaderT Int m
Test.hs:46:99: error:
• Couldn't match representation of type: m (GeneralAllocated
(ReaderT Int m) SomeException releaseReturn b a)
with that of: m (GeneralAllocated
(FooT m) SomeException releaseReturn b a)
arising from the coercion of the method ‘stateThreadingGeneralWith’
from type ‘forall releaseReturn b a.
GeneralAllocate (ReaderT Int m) SomeException releaseReturn b a
-> (a -> ReaderT Int m b) -> ReaderT Int m (b, releaseReturn)’
to type ‘forall releaseReturn b a.
GeneralAllocate (FooT m) SomeException releaseReturn b a
-> (a -> FooT m b) -> FooT m (b, releaseReturn)’
NB: We cannot know what roles the parameters to ‘m’ have;
we must assume that the role is nominal
• When deriving the instance for (MonadWith (FooT m))
|
46 | newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad) deriving MonadWith via ReaderT Int m
| ^^^^^^^^^
Same issue with:
newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad, MonadWith)
-}
newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad)
deriving via ReaderT Int m instance (MonadWith m) => MonadWith (FooT m)
newtype GeneralAllocate m e releaseReturn releaseArg a
= GeneralAllocate ((forall x. m x -> m x) -> m (GeneralAllocated m e releaseReturn releaseArg a))
data GeneralAllocated m e releaseReturn releaseArg a = GeneralAllocated
{ allocatedResource :: !a
, releaseAllocated :: !(Either e releaseArg -> m releaseReturn)
}
instance (MonadWith m) => MonadWith (ReaderT r m) where
stateThreadingGeneralWith
:: forall a b releaseReturn
. GeneralAllocate (ReaderT r m) SomeException releaseReturn b a
-> (a -> ReaderT r m b)
-> ReaderT r m (b, releaseReturn)
stateThreadingGeneralWith (GeneralAllocate allocA) go = ReaderT $ \r -> do
let
allocA' :: (forall x. m x -> m x) -> m (GeneralAllocated m SomeException releaseReturn b a)
allocA' restore = do
let
restore' :: forall x. ReaderT r m x -> ReaderT r m x
restore' mx = ReaderT $ restore . runReaderT mx
GeneralAllocated a releaseA <- runReaderT (allocA restore') r
let
releaseA' relTy = runReaderT (releaseA relTy) r
pure $ GeneralAllocated a releaseA'
stateThreadingGeneralWith (GeneralAllocate allocA') (flip runReaderT r . go)
```
## Expected behavior
Both of these should behave the same, and should both work. I think this should also work with `deriving newtype` instead of having to specify the `via` type.
## Environment
* GHC version used: 9.2.4, 9.8.1https://gitlab.haskell.org/ghc/ghc/-/issues/24155Make DeriveFunctor Bifunctor-aware2023-11-07T15:08:52ZIcelandjackMake DeriveFunctor Bifunctor-awareCan we integrate Bifunctor into the DeriveFunctor pipeline since Bifunctor is in base?
When it encounters a binary type constructor `Bi ?x ?y`, if the last type argument appears (positively) in `?x` it should check whether `Bifunctor Bi...Can we integrate Bifunctor into the DeriveFunctor pipeline since Bifunctor is in base?
When it encounters a binary type constructor `Bi ?x ?y`, if the last type argument appears (positively) in `?x` it should check whether `Bifunctor Bi` holds and use `bimap` to map over it.
```haskell
{-# Language DeriveFunctor #-}
{-# Language DerivingStrategies #-}
-- • Can't make a derived instance of
-- ‘Functor Foo’ with the stock strategy:
-- Constructor ‘F’ must use the type variable only as the last argument of a data type
-- • In the newtype declaration for ‘Foo’
newtype Foo a = Foo (Either [a] [a])
deriving stock Functor
```
This should to derive `Functor (Function іn)`, assuming the appropriate functors.
```haskell
type Function :: Type -> Type -> Type
newtype Function іn out = Functor
{ funcApply :: FData -> [Exp іn] -> Either (RawException out) (Exp out)
}
deriving stock Functor
```https://gitlab.haskell.org/ghc/ghc/-/issues/23782Derived Enum instances should perform tagToEnum# eagerly2024-03-28T11:36:36ZMatthew Cravenclyring@gmail.comDerived Enum instances should perform tagToEnum# eagerlyCurrently, the code for `enumFrom` in a derived `Enum` instance looks like this:
```haskell
enumFrom a_aF3
= case (GHC.Prim.dataToTag# a_aF3) of
a#_aF4
-> GHC.Base.map
$tag2con_W_aEV
...Currently, the code for `enumFrom` in a derived `Enum` instance looks like this:
```haskell
enumFrom a_aF3
= case (GHC.Prim.dataToTag# a_aF3) of
a#_aF4
-> GHC.Base.map
$tag2con_W_aEV
(GHC.Enum.enumFromTo (GHC.Types.I# a#_aF4) $maxtag_W_aEW)
```
Here, `$tag2con_W_aEV` is a trivial wrapper around the `tagToEnum#` primop, which can segfault if called with a bad argument. In this case, every element of `GHC.Enum.enumFromTo (GHC.Types.I# a#_aF4) $maxtag_W_aEW` is a valid argument, so we would really like to eagerly evaluate this `$tag2con_W_aEV` call when building each cons cell in the result list rather than producing a thunk. This means using a stricter version of `map`.
Today, the desired eager evaluation actually does happen, but for completely the wrong reason: `tagToEnum#` is (incorrectly) not marked as `can_fail`, so the compiler sees no danger in speculatively evaluating it in CorePrep. But this omission will be fixed in !10097, causing a performance regression until the derived code is fixed.
(All of the above applies to `enumFromThen` as well as to `enumFrom`.)9.10.1https://gitlab.haskell.org/ghc/ghc/-/issues/23496Shortcomings in dependency analysis of deriving clauses2023-08-03T12:14:41Zsheafsam.derbyshire@gmail.comShortcomings in dependency analysis of deriving clauses```haskell
{-# LANGUAGE DerivingStrategies, GeneralisedNewtypeDeriving #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module T23496 where
import Data...```haskell
{-# LANGUAGE DerivingStrategies, GeneralisedNewtypeDeriving #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module T23496 where
import Data.Kind
class C a where
type T a :: Type
instance C Int where
type T Int = Bool
newtype N = MkN Int
deriving newtype C
--deriving instance C N
--instance C N where
-- type T N = T Int
type F :: forall a. T a -> Type
type family F a where
F @Int True = Float
F @N False = Double
```
With either of the derived instances (standalone or non-standalone), we get a type error:
```
error: [GHC-83865]
• Expected kind ‘T N’, but ‘False’ has kind ‘Bool’
• In the second argument of ‘F’, namely ‘False’
In the type family declaration for ‘F’
|
28 | F @N False = Double
| ^^^^^
```
Changing it to an user-written instance (comment out the deriving clause and uncomment the instance), the program compiles fine.
I think the problem is that we separate out derived instances from user-written instances in `tcTyClsInstDecls`; they should instead be handled together as they might depend on eachother.Ryan ScottRyan Scotthttps://gitlab.haskell.org/ghc/ghc/-/issues/23494"Deriving" plugins2023-06-13T18:04:00Zsheafsam.derbyshire@gmail.com"Deriving" pluginsI think it would be nice if the `deriving` infrastructure was extensible with plugins. This will probably need a GHC proposal in the end, but first I'd like to figure out how we would go about it.
I imagine it being a new deriving strat...I think it would be nice if the `deriving` infrastructure was extensible with plugins. This will probably need a GHC proposal in the end, but first I'd like to figure out how we would go about it.
I imagine it being a new deriving strategy which specifies a plugin name (module name):
```haskell
data P a = P3 a a a
deriving (Semigroup, Monoid)
plugin MyPlugin
```
In [Sam's plugin](https://github.com/sheaf/sams-plugin), I hijacked the `deriving via` mechanism to emulate this within a typechecking plugin:
```haskell
{-# OPTIONS_GHC -fplugin=Sam'sPlugin #-}
import Sam'sOptics
data D a = MkD { fld1 :: a, fld2 :: Int# }
deriving Sam'sOptics via Sam'sPlugin
```
This would create a collection of `HasField` instances:
```haskell
instance HasField "fld1" (D a) a
instance HasField "fld2" (D a) Int#
```
(for a custom `HasField` class which is representation-polymorphic).
The problem with this approach is that a typechecking plugin can't add instances to the instance environment without a lot of hacks. If we instead integrate it with `deriving`, then plugin-written instances will work as usual, without special logic.
One difficulty in the above example, which I believe is a key usability feature, is that we wrote a single deriving clause but it derived a bunch of instances. It would be quite onerous, for a record with many fields, to have to derive all of the `HasField` instances individually.https://gitlab.haskell.org/ghc/ghc/-/issues/23462"Instance head is not headed by a class" error message is less clear than mes...2023-06-17T07:12:31ZGregory Gerasev"Instance head is not headed by a class" error message is less clear than message for type family use corner case## Summary
Consider this code:
```
data MyType
type MyConstraint x = Eq x
deriving stock instance MyConstraint MyType
```
GHC produces clear explanation in error message:
```
Illegal instance for a type synonym
A class instance must ...## Summary
Consider this code:
```
data MyType
type MyConstraint x = Eq x
deriving stock instance MyConstraint MyType
```
GHC produces clear explanation in error message:
```
Illegal instance for a type synonym
A class instance must be for a class
```
But when I run this:
> deriving stock instance forall (c :: Type -> Constraint). c MyType
Or even more simple example:
> deriving stock instance forall (c :: Constraint). c
GHC produces much less clear comment:
> Instance head is not headed by a class: c MyType
It seems to me that the problem is not related to "headed by a class".
Trick with `(c' ~ c MyType, ..` does not work in that case.
Why would one need this code?
I tried to derive `Some` wrapper constraints from wrapped class.
In my case:
```
import Data.Kind
data EntityKind x -- Ommited GADT
class Entity x -- Ommited class
data Some (container :: Type -> Type)
= forall entity.
Entity entity =>
MkSome (EntityKind entity) (container entity)
deriving stock instance
forall (x :: Constraint) (c :: Type -> Constraint) (container :: Type -> Type) .
(forall entity. (Entity entity, c entity) => c (container entity), c (Some container) ~ x) => x
```
## Proposed improvements or changes
I do not know. If `forall c.` does not make sense, than not clear error is probably not important.
But my deriving case seems possible in principle. Is not it?
Maybe I am missing some previous issues, but they did not take such `QuantifiedConstraints` usage into account.
## Environment
* GHC version used: 9.6.1https://gitlab.haskell.org/ghc/ghc/-/issues/23438`TypeError` thrown in standalone `via` deriving but not attached `via` deriving2023-05-31T18:33:03Zparsonsmatt`TypeError` thrown in standalone `via` deriving but not attached `via` deriving## Summary
A `newtype` for `via` deriving is provided that gives a `TypeError`, effectively banning the instance from use, but with a nice error message. We use this at work to prevent folks from running database operations in the "main...## Summary
A `newtype` for `via` deriving is provided that gives a `TypeError`, effectively banning the instance from use, but with a nice error message. We use this at work to prevent folks from running database operations in the "main" monad, and instead guide them towards `runDB` which handles transactions.
## Steps to reproduce
```haskell
{-# language DerivingVia #-}
{-# language DataKinds #-}
{-# language UndecidableInstances #-}
{-# language StandaloneDeriving #-}
module Woops where
import GHC.TypeLits
class C a where
f :: a -> a
newtype Can'tBeC a = Can'tBeC a
instance ( TypeError ('Text "No")) => C (Can'tBeC a) where
f = error "unreachable"
instance C Int where f = id
data Good = Good
deriving C via (Can'tBeC Good)
data Bad = Bad
deriving via Can'tBeC Bad instance C Bad
```
With GHC 9.6.1, I get the following result:
```
λ ~/Projects/ghc-woops/ master* cabal build
Build profile: -w ghc-9.6.1 -O1
In order, the following will be built (use -v for more details):
- ghc-woops-0.1.0.0 (lib) (file src/Woops.hs changed)
Preprocessing library for ghc-woops-0.1.0.0..
Building library for ghc-woops-0.1.0.0..
[2 of 2] Compiling Woops ( src/Woops.hs, /home/matt/Projects/ghc-woops/dist-newstyle/build/x86_64-linux/ghc-9.6.1/ghc-woops-0.1.0.0/build/Woops.o, /
home/matt/Projects/ghc-woops/dist-newstyle/build/x86_64-linux/ghc-9.6.1/ghc-woops-0.1.0.0/build/Woops.dyn_o ) [Source file changed]
src/Woops.hs:25:1: error: [GHC-64725]
• No
• In the third argument of ‘ghc-prim-0.10.0:GHC.Prim.coerce’, namely
‘(f @(Can'tBeC Bad))’
In the expression:
ghc-prim-0.10.0:GHC.Prim.coerce
@(Can'tBeC Bad -> Can'tBeC Bad) @(Bad -> Bad) (f @(Can'tBeC Bad))
In an equation for ‘f’:
f = ghc-prim-0.10.0:GHC.Prim.coerce
@(Can'tBeC Bad -> Can'tBeC Bad) @(Bad -> Bad) (f @(Can'tBeC Bad))
When typechecking the code for ‘f’
in a derived instance for ‘C Bad’:
To see the code I am typechecking, use -ddump-deriv
|
25 | deriving via Can'tBeC Bad instance C Bad
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
This is consistent going back to GHC 8.10.7.
## Expected behavior
I expect that the `StandaloneDeriving` and attached deriving have the same behavior.
But, to be totally specific, I do want both instances to be *accepted*. The purpose of providing the `TypeError` instance here is to provide helpful error messages to folks that are using these, so instead of seeing `No instance for C Bad` they see `You need to do X if you want to use C in the Bad type` or similar.
## Environment
* GHC version used: 9.6.1, 9.4.5, 9.2.7, 8.10.7
Optional:
* Operating System: Ubuntu
* System Architecture: x86sheafsam.derbyshire@gmail.comsheafsam.derbyshire@gmail.comhttps://gitlab.haskell.org/ghc/ghc/-/issues/23329"Iface type variable out of scope" with poly-kinded associated type and newty...2023-08-04T00:08:51Zgelisam"Iface type variable out of scope" with poly-kinded associated type and newtype-deriving## Summary
When a newtype-derived instance involving a kind-polymorphic associated type is called and optimized, ghc-9.4.2 prints the following error:
```
typecheckIface
Declaration for $fMyClassTYPEMyMaybe
Unfolding of $fMyClassTYPEMy...## Summary
When a newtype-derived instance involving a kind-polymorphic associated type is called and optimized, ghc-9.4.2 prints the following error:
```
typecheckIface
Declaration for $fMyClassTYPEMyMaybe
Unfolding of $fMyClassTYPEMyMaybe:
Iface type variable out of scope: k
<no location info>: error:
Cannot continue after interface file error
```
There are two parts to this error message: the `Iface type variable out of scope` part is a regression introduced in ghc-9.0, while the `Cannot continue after interface file error` part is a regression introduced in ghc-9.4.
I found several issues from years ago featuring this error message, triggered by increasingly-obscure circumstances. This is yet another obscure circumstance. #9263 is the most closely related, as it also involves associated types and polykinds, but not newtype-deriving. #2018 is also related in that it also requires optimizations to be turned on.
Note that some of those tickets say that the error only occurs the _second_ time the code is built. This is not the case here, the error occurs the first time.
## Steps to reproduce
The easiest way to reproduce the problem is to clone https://github.com/gelisam/ghc-9.4-bug and to run `./build.sh`. Alternatively, create those two modules:
```haskell
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module MyModule where
import Data.Kind (Type)
import Data.Proxy (Proxy)
class MyClass (f :: k -> Type) where
type MyTypeFamily f (i :: k) :: Type
myMethod :: MyTypeFamily f i -> Proxy f -> Proxy i -> ()
instance MyClass Maybe where
type MyTypeFamily Maybe i = ()
myMethod = undefined
newtype MyMaybe a = MyMaybe (Maybe a)
deriving MyClass
```
and
```haskell
{-# LANGUAGE TypeApplications #-}
module Foo where
import Data.Kind (Type)
import Data.Proxy (Proxy(Proxy))
import MyModule
foo :: ()
foo = myMethod @Type @MyMaybe @() () Proxy Proxy
```
Then compile those two modules, with optimizations turned on:
```bash
$ ghc -O MyModule.hs Foo.hs
[2 of 2] Compiling Foo ( Foo.hs, Foo.o )
typecheckIface
Declaration for $fMyClassTYPEMyMaybe
Unfolding of $fMyClassTYPEMyMaybe:
Iface type variable out of scope: k
<no location info>: error:
Cannot continue after interface file error
```
## Expected behavior
I expect the two modules to compile successfully.
## Environment
* GHC version used: ghc-9.4.2, ghc-9.2.4, ghc-9.0.2
Of these, only ghc-9.4 fails to compile.
With ghc-9.2 and ghc-9.0, the `Iface type variable out of scope` message is printed but not `Cannot continue after interface file error`, and the module still compiles successfully.
I have also tried with ghc-8.10.7, which does not print either message.
Optional:
* Operating System: macOS Ventura 13.3
* System Architecture: arm649.6.2Ryan ScottRyan Scotthttps://gitlab.haskell.org/ghc/ghc/-/issues/21725Derived Read instance of record containing phantom type is incorrect2022-06-20T08:58:09ZRoosembert PalaciosDerived Read instance of record containing phantom type is incorrect## Summary
Given a phantom type with custom read/show instances, when wrapped inside a record, the derived read instance is incorrect.
## Steps to reproduce
Given the following code:
```haskell
{-# LANGUAGE KindSignatures #-}
{-# LAN...## Summary
Given a phantom type with custom read/show instances, when wrapped inside a record, the derived read instance is incorrect.
## Steps to reproduce
Given the following code:
```haskell
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds #-}
import GHC.TypeLits
import Text.Read
import Text.ParserCombinators.ReadP (string)
import Data.Proxy
data Phantom (a :: Symbol) = Single
instance forall s. KnownSymbol s => Show (Phantom s) where
show Single = symbolVal (Proxy @s)
instance KnownSymbol s => Read (Phantom s) where
readPrec = readP_to_Prec $ \_ -> string (show $ Single @s) >> pure Single
newtype Foo = Foo { f :: Phantom "casper" } deriving (Show, Read)
main = do
print (read "casper" :: Phantom "casper")
print (Foo Single)
print (read "Foo {f = casper}" :: Foo)
```
Running this code produces an exception:
```console
$ runhaskell bug.hs
casper
Foo {f = casper}
Foo {f = bug.hs: Prelude.read: no parse
```
## Expected behavior
This code should not crash.
## Environment
* GHC version used: 9.0.2.
Optional:
* Operating System: NixOS 22.11.20220604.231efec (Raccoon)
* System Architecture: x86_64 GNU/LinuxBen GamariBen Gamarihttps://gitlab.haskell.org/ghc/ghc/-/issues/21679`deriving newtype Integral` for Integer newtypes invokes `Widentities` in ghc-92023-09-07T09:30:54Z— —`deriving newtype Integral` for Integer newtypes invokes `Widentities` in ghc-9## Summary
Attempting to derive a newtype `Integral` instance for a `newtype` that wraps an `Integer` produces a `-Widentitites` warning.
## Steps to reproduce
Starting from a Test.hs:
```haskell
{-# LANGUAGE DerivingStrategies #-}
{-...## Summary
Attempting to derive a newtype `Integral` instance for a `newtype` that wraps an `Integer` produces a `-Widentitites` warning.
## Steps to reproduce
Starting from a Test.hs:
```haskell
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Test where
newtype I = I Integer
deriving newtype (Enum, Num, Eq, Ord, Real, Integral)
```
and running e.g. `stack ghc Test.hs --ghc-options "-Werror=identities"` (given a `stack.yaml` targeted at a ghc-9.0.2 or ghc-9.2.2) results in:
```
GHCi, version 9.0.2: https://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Test ( /home/mrkun/serokell/ghc-test/Test.hs, interpreted )
/home/mrkun/serokell/ghc-test/Test.hs:7:47: error: [-Widentities, -Werror=identities]
Call of toInteger :: Integer -> Integer
can probably be omitted
|
7 | deriving newtype (Enum, Num, Eq, Ord, Real, Integral)
| ^^^^^^^^
Failed, no modules loaded.
```
## Expected behavior
No errors or warnings reported; e.g. same setup but with a ghc-8.10.7 produces:
```
GHCi, version 8.10.7: https://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Test ( /home/mrkun/serokell/ghc-test/Test.hs, interpreted )
Ok, one module loaded.
```
## Environment
* GHC version used: 9.0.2; 9.2.2
* Operating System: Linux (Ubuntu 18.04.1)
* System Architecture: x86_64https://gitlab.haskell.org/ghc/ghc/-/issues/20815Make coerce-derived dictionaries coercible2023-12-15T08:14:25ZIcelandjackMake coerce-derived dictionaries coercibleTake the [`Alt`](https://hackage.haskell.org/package/semigroupoids-5.3.6/docs/Data-Functor-Alt.html) type class as an example, which is `Alternative` without the `empty` method:
```haskell
{-# Language ConstrainedClassMethods #-}
{-# La...Take the [`Alt`](https://hackage.haskell.org/package/semigroupoids-5.3.6/docs/Data-Functor-Alt.html) type class as an example, which is `Alternative` without the `empty` method:
```haskell
{-# Language ConstrainedClassMethods #-}
{-# Language DerivingVia #-}
import Control.Applicative
class Functor f => Alt f where
(<!>) :: f a -> f a -> f a
some :: Applicative f => f a -> f [a]
some v = some_v
where many_v = some_v <!> pure []
some_v = (:) <$> v <*> many_v
many :: Applicative f => f a -> f [a]
many v = many_v
where many_v = some_v <!> pure []
some_v = (:) <$> v <*> many_v
```
The applicative constraints on the methods means we cannot derive `Alt`.
If we try to derive it (say via `[]`) GHC fails to coerce between `Applicative []` and `Applicative List`.
```haskell
instance Alt [] where
(<!>) = (<>)
newtype List a = List [a]
-- • Couldn't match type ‘[]’ with ‘List’
-- arising from the coercion of the method ‘many’
-- from type ‘forall a. Applicative [] => [a] -> [[a]]’
-- to type ‘forall a. Applicative List => List a -> List [a]’
-- • When deriving the instance for (Alt List)
--
-- .. same for ‘some’
deriving (Functor, Applicative, Alt)
via []
```
GHC can make use of the provenence of the `Applicative` instance: we know that that `Applicative List` was derived via `Applicative []` using `coerce`-based deriving (either `newtype`, `via`) and so the dictionaries can be considered `Coercible` (?). If not I'm curious if this fails.¹
This is not the first time I want this (I forgot in what context though), let me know if it's sensible/useful.
----
¹ Instead of coercing method-by-method could `coerce`-based deriving coerce the whole dictionary?https://gitlab.haskell.org/ghc/ghc/-/issues/20314feature request: deriving for Data.Bifoldable Data.Bifunctor Data.Bitraversable2023-11-05T17:35:34ZCarter Schonwaldfeature request: deriving for Data.Bifoldable Data.Bifunctor Data.Bitraversablenow that Bifoldable,Bifunctor, and Bitraversable are in base, whats the best path forward to add ghc deriving support to them? (or are there good reasons that wouldn't make sense?)
@core-libraries
@RyanGlScott
@Icelandjacknow that Bifoldable,Bifunctor, and Bitraversable are in base, whats the best path forward to add ghc deriving support to them? (or are there good reasons that wouldn't make sense?)
@core-libraries
@RyanGlScott
@IcelandjackResearch neededhttps://gitlab.haskell.org/ghc/ghc/-/issues/20099Inflexible DerivingVia2021-09-28T19:24:24ZJaro ReindersInflexible DerivingVia## Summary
I recently wanted to derive instances like `Functor` and `Applicative` for a datatype that has an `Arrow` instance. I thought this would be possible by deriving via the `ArrowMonad` type. Unfortunately this doesn't work.
## ...## Summary
I recently wanted to derive instances like `Functor` and `Applicative` for a datatype that has an `Arrow` instance. I thought this would be possible by deriving via the `ArrowMonad` type. Unfortunately this doesn't work.
## Steps to reproduce
Here is a minimal reproducer:
```haskell
{-# LANGUAGE DerivingVia #-}
import Control.Arrow
import Control.Category
data Test a b = Test a b
deriving Functor via ArrowMonad Test
-- details of these instances don't matter
instance Category Test
instance Arrow Test
```
This gives two related errors:
```
Sad.hs:6:35: error:
• Couldn't match representation of type ‘a’ with that of ‘()’
arising from the coercion of the method ‘<$’
from type ‘forall a b. a -> ArrowMonad Test b -> ArrowMonad Test a’
to type ‘forall a1 b. a1 -> Test a b -> Test a a1’
‘a’ is a rigid type variable bound by
the deriving clause for ‘Functor (Test a)’
at Sad.hs:6:35-41
• When deriving the instance for (Functor (Test a))
|
6 | data Test a b = Test a b deriving Functor via ArrowMonad Test
| ^^^^^^^
Sad.hs:6:35: error:
• Couldn't match representation of type ‘a’ with that of ‘()’
arising from the coercion of the method ‘fmap’
from type ‘forall a b.
(a -> b) -> ArrowMonad Test a -> ArrowMonad Test b’
to type ‘forall a1 b. (a1 -> b) -> Test a a1 -> Test a b’
‘a’ is a rigid type variable bound by
the deriving clause for ‘Functor (Test a)’
at Sad.hs:6:35-41
• When deriving the instance for (Functor (Test a))
|
6 | data Test a b = Test a b deriving Functor via ArrowMonad Test
| ^^^^^^^
```
## Expected behavior
It does kind of make sense, but I hoped the deriving mechanism would generate instances like this:
```haskell
instance Functor (Test ()) where
fmap :: forall a b. (a -> b) -> Test () a -> Test () b
fmap = coerce (fmap @(ArrowMonad Test) @a @b)
```
The `Test ()` could be inferred from the definition of `ArrowMonad`, namely:
```haskell
newtype ArrowMonad a b = ArrowMonad (a () b)
```
So `ArrowMonad Test a` has the same representation as `Test () a`.
I guess I don't really know if this is a bug or a feature request because this instance does require `FlexibleInstances` which might complicate things.
## Workaround
A workaround is to use standalone deriving, but that can get verbose, this is an example from a parser arrow and monad that I'm working on:
```haskell
newtype Parser a b = Parser { unParser :: (a, String) -> Steps (b, String) }
deriving (Category, Arrow, ArrowZero, ArrowPlus, ArrowChoice, ArrowApply)
via StateArrow String (Kleisli Steps)
deriving via ArrowMonad Parser instance Functor (Parser ())
deriving via ArrowMonad Parser instance Applicative (Parser ())
deriving via ArrowMonad Parser instance Alternative (Parser ())
deriving via ArrowMonad Parser instance Monad (Parser ())
deriving via ArrowMonad Parser instance MonadPlus (Parser ())
```
Another alternative is to change `ArrowMonad` to expose another type parameter:
```haskell
newtype ArrowMonad' a b c = ArrowMonad' (a b c)
```
(only the `Monad` instance needs a small change)
Then I could write:
```haskell
newtype Parser a b = Parser { unParser :: (a, String) -> Steps (b, String) }
deriving (Category, Arrow, ArrowZero, ArrowPlus, ArrowChoice, ArrowApply)
via StateArrow String (Kleisli Steps)
deriving (Functor, Applicative, Alternative, Monad, MonadPlus)
via ArrowMonad' Parser a
```
## Environment
* GHC version used: 8.10.4
Optional:
* Operating System: NixOS 21.05
* System Architecture: x86_64https://gitlab.haskell.org/ghc/ghc/-/issues/19936Odd GND failure/success2021-06-08T14:22:57ZDavid FeuerOdd GND failure/successThere's currently a discussion about whether to change the definition of `MonadTrans` to use `QuantifiedConstraints`:
```haskell
class (forall m. Monad m => Monad (t m)) => MonadTrans t where
lift :: m a -> t m a
```
I wondered if th...There's currently a discussion about whether to change the definition of `MonadTrans` to use `QuantifiedConstraints`:
```haskell
class (forall m. Monad m => Monad (t m)) => MonadTrans t where
lift :: m a -> t m a
```
I wondered if this works with GND. As it turns out, the answer is yes:
```haskell
newtype B t (m :: Type -> Type) a => B (t m a)
deriving (Functor, Applicative, Monad)
-- StandaloneDeriving just to be sure the constraints is
-- what I expect.
deriving instance MonadTrans t => MonadTrans (B t)
```
compiles just fine. But a conceptually very similar idea does not:
```haskell
class MonadTrans t where
lift :: m a -> t m a
liftMonad :: Monad m => Proxy (t m) -> (Monad (t m) => r) -> r
```
This one produces an error,
```
Lift.hs:9:42: error:
• Occurs check: cannot construct the infinite type: t ~ B t
arising from the coercion of the method ‘liftMonad’
from type ‘forall (m :: * -> *) r.
Monad m =>
Proxy (t m) -> (Monad (t m) => r) -> r’
to type ‘forall (m :: * -> *) r.
Monad m =>
Proxy (B t m) -> (Monad (B t m) => r) -> r’
• When deriving the instance for (MonadTrans (B t))
```
Why does one work and not the other? Is there a good reason for that, or should they both succeed or both fail?https://gitlab.haskell.org/ghc/ghc/-/issues/19906Should Generically (/ Generically1) derive newtype Generic (/ Generic1)?2021-05-30T15:01:27ZIcelandjackShould Generically (/ Generically1) derive newtype Generic (/ Generic1)?Regarding #17147 (!5726), this may be controversial and touch on the philosophy of Generic. "Generic" is universally derived using the *stock* deriving strategy. I propose deriving those instances for `Generically{,1}` using generalized ...Regarding #17147 (!5726), this may be controversial and touch on the philosophy of Generic. "Generic" is universally derived using the *stock* deriving strategy. I propose deriving those instances for `Generically{,1}` using generalized *newtype* deriving, by reusing the underlying `Generic{,1}` instances. This is in line with all other instances for `Generically{,1}` as they are defined using the generic representation of the underlying type.
```haskell
type Generically :: Type -> Type
newtype Generically a = Generically a
deriving
newtype Generic
type Generically1 :: (k -> Type) -> k -> Type
newtype Generically1 f a = Generically1 (f a)
deriving
newtype Generic1
```
That is to say be given the following instance
```haskell
instance Generic a => Generic (Generically a) where
type Rep (Generically a) = Rep a
from :: Generically a -> Rep a b
from (Generically a) = from a
to :: Rep a b -> Generically a
to = Generically . to
instance Generic1 f => Generic1 (Generically1 f) where
type Rep1 (Generically1 f) = Rep1 f
from1 :: Generically1 f a -> Rep1 f a
from1 (Generically1 as) = from1 as
to1 :: Rep1 f a -> Generically1 f a
to1 = Generically1 . to1
```
The other instances avoid newtype wrapping
```haskell
Generically a <> Generically b = Generically (to (from a <> from b :: Rep a ()))
mempty = Generically (to (mempty :: Rep a ()))
fmap f (Generically1 as) = Generically1 (to1 (fmap f (from1 as)))
a <$ Generically1 as = Generically1 (to1 (a <$ from1 as))
pure a = Generically1 (to1 (pure a))
Generically1 fs <*> Generically1 as = Generically1 (to1 (from1 fs <*> from1 as))
liftA2 (·) (Generically1 as) (Generically1 bs) = Generically1 (to1 (liftA2 (·) (from1 as) (from1 bs)))
empty = Generically1 (to1 empty)
Generically1 as1 <|> Generically1 as2 = Generically1 (to1 (from1 as1 <|> from1 as2))
```
--->
```haskell
a <> b = to (from a <> from b :: Rep a ())
mempty = to (mempty :: Rep a ())
fmap f = to1 . fmap f . from1
a <$ as = to1 (a <$ from1 as)
pure = to1 . pure
fs <*> as = to1 (from1 fs <*> from1 as)
liftA2 (·) as bs = to1 (liftA2 (·) (from1 as) (from1 bs))
empty = to1 empty
as1 <|> as2 = to1 (from1 as1 <|> from1 as2)
```https://gitlab.haskell.org/ghc/ghc/-/issues/19713DeriveFunctor: Use `contramap' in contravariant position2021-04-19T03:44:35ZIcelandjackDeriveFunctor: Use `contramap' in contravariant positionThis rule for [*cofmap* in `GHC.Tc.Deriv.Functor`](https://gitlab.haskell.org/ghc/ghc/-/blob/master/compiler/GHC/Tc/Deriv/Functor.hs#L111) can not deal with a type variable in contravariant position
```
$(cofmap 'a 'b x) = x ...This rule for [*cofmap* in `GHC.Tc.Deriv.Functor`](https://gitlab.haskell.org/ghc/ghc/-/blob/master/compiler/GHC/Tc/Deriv/Functor.hs#L111) can not deal with a type variable in contravariant position
```
$(cofmap 'a 'b x) = x -- when b does not contain a
$(cofmap 'a 'a x) = error "type variable in contravariant position"
$(cofmap 'a '(b1,b2) x) = case x of (x1,x2) -> ($(cofmap 'a 'b1) x1, $(cofmap 'a 'b2) x2)
$(cofmap 'a '(T b1 a) x) = error "type variable in contravariant position" -- when a only occurs directly as the last argument of T
$(cofmap 'a '(T b1 b2) x) = fmap (\y. $(cofmap 'a 'b2 y)) x -- when a only occurs in the last parameter, b2
$(cofmap 'a '(tb -> tc) x) = \(y:tb[b/a]) -> $(cofmap 'a' 'tc' (x $(fmap 'a 'tb y)))
```
but now that we have `Data.Functor.Contravariant` in base why not generate an invocation to `contramap`?
```
$(cofmap 'a '(T b1 a) x) = contramap f x
```
As an example, this works becuase of the special case for functions
```
> :set -XDerivingStrategies -XDeriveFunctor
> newtype F a = MkF ((a -> Int) -> Int) deriving stock Functor
```
but if we use `Op Int a` (which is a newtype wrapping `a -> Int`) ghc does not know how to derive it.
```
> newtype F a = MkF (Op Int a -> Int) deriving stock Functor
<interactive>:10:52: error:
• Can't make a derived instance of
‘Functor F’ with the stock strategy:
Constructor ‘MkF’ must not use the type variable in a function argument
• In the newtype declaration for ‘F’
```
I claim it should work by inserting a `contramap` invocation there so both these definitions
```haskell
newtype F a = MkF (Op Int a -> Int) deriving stock Functor
newtype F a = MkF (Equivalence a -> Int) deriving stock Functor
```
would generate the following (basically)
```haskell
instance Functor F where
fmap :: (a -> a') -> (F a -> F a')
fmap f (MkF as) = MkF (fmap as (contramap f))
```https://gitlab.haskell.org/ghc/ghc/-/issues/19707[Discussion] Integrating DataTypeable, Functor, Foldable, Traversable, Generi...2023-06-10T10:51:12ZHécate Moonlight[Discussion] Integrating DataTypeable, Functor, Foldable, Traversable, Generic and Lift in the `stock` deriving strategyI wish to start a discussion around the possibility of integrating the numerous classes we offer in `base` in the `stock` deriving strategy by the removing the need to use language extensions to expose them to end-user.
This ticket is n...I wish to start a discussion around the possibility of integrating the numerous classes we offer in `base` in the `stock` deriving strategy by the removing the need to use language extensions to expose them to end-user.
This ticket is not meant to bypass a proper GHC Proposal, but I'd like to get some feedback from the GHC dev team and adjacent folks before starting a proposal process.
I personally think we may have reached the maturity where it would make sense to greatly simplify this aspect of the compiler, but I am not omniscient and clearly could use different perspectives. :)
---
Note: Matt Parsons has also raised this question in his article [Haskell Proposal: Simplify Deriving](https://www.parsonsmatt.org/2020/11/10/simplifying_deriving.html).https://gitlab.haskell.org/ghc/ghc/-/issues/19445(Derived) Ord instances generate terrible code2022-11-22T09:59:00ZSebastian Graf(Derived) Ord instances generate terrible codeConsider these attempts at coming up with more compact `compare` functions:
```hs
{-# OPTIONS_GHC -O2 -fforce-recomp #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
module Lib (foo, bar, b...Consider these attempts at coming up with more compact `compare` functions:
```hs
{-# OPTIONS_GHC -O2 -fforce-recomp #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
module Lib (foo, bar, baz) where
import GHC.Exts
import GHC.Classes
data U = U Int deriving Eq
instance Ord U where
-- so that this function isn't WW'd
compare ~(U a) ~(U b) = lazy $ compare a b
{-# NOINLINE compare #-}
data T
= T0 U
| T1 U
| T2 U
| T3 U
| T4 U
| T5 U
deriving (Eq, Ord)
foo :: T -> T -> Ordering
foo = compare @T
bar :: T -> T -> Ordering
bar !_ r | !_ <- dataToTag# r, id False = undefined
bar (T0 a1) (T0 a2) = compare a1 a2
bar (T1 a1) (T1 a2) = compare a1 a2
bar (T2 a1) (T2 a2) = compare a1 a2
bar (T3 a1) (T3 a2) = compare a1 a2
bar (T4 a1) (T4 a2) = compare a1 a2
bar (T5 a1) (T5 a2) = compare a1 a2
bar l r
| isTrue# (dataToTag# l <# dataToTag# r) = LT
| otherwise = GT
baz :: T -> T -> Ordering
baz l r = compareInt# (dataToTag# l) (dataToTag# r) <> go l r
where
go (T0 a1) (T0 a2) = compare a1 a2
go (T1 a1) (T1 a2) = compare a1 a2
go (T2 a1) (T2 a2) = compare a1 a2
go (T3 a1) (T3 a2) = compare a1 a2
go (T4 a1) (T4 a2) = compare a1 a2
go (T5 a1) (T5 a2) = compare a1 a2
```
`bar` speculates `dataToTag#` on the second argument, so that its result is shared among all case branches. `baz` goes a step further: It simply compares tags and only compares fields when the tags match.
Here's the generated Core for the `T3` case:
```
foo a b_aLh = case a of {
T3 a1_aLp ->
case dataToTag# @T b_aLh of b#_aLq { __DEFAULT ->
case <# b#_aLq 3# of {
__DEFAULT ->
case b_aLh of {
__DEFAULT -> GHC.Types.LT;
T3 b1_aLr -> Lib.foo_$ccompare a1_aLp b1_aLr
};
1# -> GHC.Types.GT
}
}
}
bar a b_aLh =
case dataToTag# @T b_aLh of ds1_dU3 { __DEFAULT ->
case a of {
T3 a1_aJb ->
case b_aLh of {
__DEFAULT ->
case <# 3# ds1_dU3 of {
__DEFAULT -> GHC.Types.GT;
1# -> GHC.Types.LT
};
T3 a2_aJc -> Lib.foo_$ccompare a1_aJb a2_aJc
};
}
}
baz l_aJj r_aJk =
case dataToTag# @T r_aJk of wild_X1 { __DEFAULT ->
case dataToTag# @T l_aJj of wild1_X2 { __DEFAULT ->
case <# wild1_X2 wild_X1 of {
1# -> GHC.Types.LT;
__DEFAULT ->
case ==# wild1_X2 wild_X1 of {
__DEFAULT -> GHC.Types.GT;
1# ->
case l_aJj of {
T3 a1_aJs ->
case r_aJk of {
__DEFAULT -> Lib.baz1; -- pattern match error thunk
T3 a2_aJt -> Lib.foo_$ccompare a1_aJs a2_aJt
};
}
}
}
```
That is I think about the Core I'd expect, with one exception: I'd hoped that GHC would detect that the pattern match error in `baz` is impossible, which it doesn't.
Although `baz` looks rather big, I expected most of it to lower to very simple instructions. Well, `nm --print-size --size-sort test.o` reveals:
```
0000000000001180 000000000000032f T Lib_bar_info
0000000000000b00 0000000000000373 T Lib_foo_info
00000000000014c8 000000000000038f T Lib_bazz_info
```
So `bar` is a bit smaller than `foo` (the stock derived function), while `baz` is larger. All of them are quite large: They range between 0x300 (768) and 0x400 (1024) bytes!
I had a cursory look at the Cmm, and I was lost almost instantly. That was due to #19444, I think.
It's surely possible to generate more compact code for derived `Ord` instances. It's so common, yet so much more inefficient to do the equivalent in C!https://gitlab.haskell.org/ghc/ghc/-/issues/19418Support overriding "No instance" errors using the default deriving strategy2021-03-02T01:20:51Zinfinity0Support overriding "No instance" errors using the default deriving strategy## Motivation
Consider this code:
```haskell
data FooState a b c d = FooState
{ partA :: a
, partB :: b
, partC :: c
, partD :: d
}
deriving (Show, Read)
withFoo :: FooState a b c d -> FooState a b c d
withFoo = undefined
...## Motivation
Consider this code:
```haskell
data FooState a b c d = FooState
{ partA :: a
, partB :: b
, partC :: c
, partD :: d
}
deriving (Show, Read)
withFoo :: FooState a b c d -> FooState a b c d
withFoo = undefined
```
Eugh, the API has 4 type params. This is confusing for users, especially when they combine it with other functions that take additional type params. In fact (a, b, c, d) are logically "our" (as the author of `FooState`) type params, so we can simplify it like this, by grouping them together:
```haskell
{-# LANGUAGE TypeFamilies #-}
class FooParams p where
type FooA p
type FooB p
type FooC p
type FooD p
data FooState' p = FooState'
{ partA :: FooA p
, partB :: FooB p
, partC :: FooC p
, partD :: FooD p
}
-- deriving (Show, Read) -- fails!
withFoo' :: FooParams p => FooState' p -> FooState' p
withFoo' = undefined
```
<details>
<summary>(Alternatively, using more advanced language features you can even avoid the typeclass context, although this prevents you defining class methods on FooA etc, click for details)
</summary>
```haskell
{-# LANGUAGE TypeFamilies, RankNTypes, PolyKinds, DataKinds, StandaloneKindSignatures #-}
data FooParams aT bT cT dT = FooParams
{ fooA :: aT
, fooB :: bT
, fooC :: cT
, fooD :: dT
}
type FooA :: forall a b c d. FooParams a b c d -> *
type family FooA p where FooA ('FooParams a b c d) = a
type FooB :: forall a b c d. FooParams a b c d -> *
type family FooB p where FooB ('FooParams a b c d) = b
type FooC :: forall a b c d. FooParams a b c d -> *
type family FooC p where FooC ('FooParams a b c d) = c
type FooD :: forall a b c d. FooParams a b c d -> *
type family FooD p where FooD ('FooParams a b c d) = d
data FooState' (p :: FooParams a b c d) = FooState'
{ partA :: FooA p
, partB :: FooB p
, partC :: FooC p
, partD :: FooD p
}
-- deriving (Show, Read) -- fails!
withFoo' :: FooState' p -> FooState' p
withFoo' = undefined
```
</details>
Either way, great, our API now only has 1 type param.
Unfortunately, now `deriving (Show, Read)` fails (in both examples):
~~~~
Test.hs:26:13: error:
• No instance for (Show (FooA p))
arising from the first field of ‘FooState'’ (type ‘FooA p’)
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
• When deriving the instance for (Show (FooState' p))
|
26 | deriving (Show, Read)
| ^^^^
Test.hs:26:19: error:
• No instance for (Read (FooA p))
arising from the first field of ‘FooState'’ (type ‘FooA p’)
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
• When deriving the instance for (Read (FooState' p))
|
26 | deriving (Show, Read)
| ^^^^
~~~~
We can currently work around this in 2 ways, neither of which is ideal:
<details>
<summary>standalone deriving, tedious manual context</summary>
```haskell
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE StandaloneDeriving, FlexibleContexts, UndecidableInstances #-}
class FooParams p where
type FooA p
type FooB p
type FooC p
type FooD p
data FooState' p = FooState'
{ partA :: FooA p
, partB :: FooB p
, partC :: FooC p
, partD :: FooD p
}
-- manually supply the context, eugh
-- increases n*m with number of associated types, and the number of extra instances e.g. Eq, Ord
deriving instance (Show (FooA p), Show (FooB p), Show (FooC p), Show (FooD p)) => Show (FooState' p)
deriving instance (Read (FooA p), Read (FooB p), Read (FooC p), Read (FooD p)) => Read (FooState' p)
withFoo' :: FooParams p => FooState' p -> FooState' p
withFoo' = undefined
```
</details>
<details>
<summary>extra type synonym, slightly ugly API</summary>
```haskell
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
class FooParams p where
type FooA p
type FooB p
type FooC p
type FooD p
-- data decl still has 4 type params
data FooState a b c d = FooState
{ partA :: a
, partB :: b
, partC :: c
, partD :: d
}
deriving (Show, Read)
-- apply the type family in a synonym, and expose this along with
-- the constructor of the original data type if necessary.
-- however, this gets ugly if the data structure is more complex
type FooState'' p = FooState (FooA p) (FooB p) (FooC p) (FooD p)
-- API methods can use the type synonym to reduce the number of params
withFoo'' :: FooParams p => FooState'' p -> FooState'' p
withFoo'' = undefined
```
</details>
## Proposal
I guess the error is a purposeful design choice to avoid surprising users - the same error occurs if e.g. using `IO p` instead of `FooA p`, and the same workarounds also work. Clearly, successfully and silently generating an instance with the context `(Show (IO p) .. ) =>`, would be surprising to users.
However for certain use-cases such as the one I just described, this "potentially-surprising" derived instance is in fact what we want, and is not surprising for these particular cases (e.g. think `Maybe p`). So it would be good if we could override the error and tell GHC to just use what it would have output. This could possibly be implemented as a new strategy with a name such as "stock_relaxed" or "stock_unchecked" or something.https://gitlab.haskell.org/ghc/ghc/-/issues/18874Deriving multiple classes in standalone (or document this limitation)2022-12-08T00:32:14ZJulien DebonDeriving multiple classes in standalone (or document this limitation)## Motivation
To the best of my knowledge (I could not find any information about it in the [GHC Standalone Deriving documentation](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#stand-alone-deriving-d...## Motivation
To the best of my knowledge (I could not find any information about it in the [GHC Standalone Deriving documentation](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#stand-alone-deriving-declarations)), it is not possible to derive multiple classes in a single standalone deriving declaration.
Repeating an identical deriving declaration only to change the derived instance seems like unnecessary boilerplate:
```haskell
deriving instance Eq (MyType 'Foo)
deriving instance Show (MyType 'Foo)
deriving instance Read (MyType 'Foo)
```
But this situation can actually become bug-prone when used together with `via` strategy, as it becomes easy to have discrepancies. Example using [deriving-aeson](https://hackage.haskell.org/package/deriving-aeson) library:
```haskell
deriving via
( CustomJSON
'[ OmitNothingFields,
UnwrapUnaryRecords,
TagSingleConstructors,
FieldLabelModifier (StripPrefix "user", CamelToSnake)
]
)
instance
ToJSON (MyType 'Foo)
deriving via
( CustomJSON
'[ OmitNothingFields, -- Oh, we forgot to add 'UnwrapUnaryRecords' here!
TagSingleConstructors,
FieldLabelModifier (StripPrefix "user", CamelToSnake)
]
)
instance
FromJSON (MyType 'Foo)
```
## Proposal
I propose GHC supports deriving several instances in a single standalone declaration.
I don't have a strong opinion on the syntax (nor have I put a lot of effort in thinking about it, I have to admit), though I give an example of what it could look like below:
```haskell
deriving instance (Eq, Show, Read) (MyType 'Foo)
deriving via
( CustomJSON
'[ OmitNothingFields,
UnwrapUnaryRecords,
TagSingleConstructors,
FieldLabelModifier (StripPrefix "user", CamelToSnake)
]
)
instance
(ToJSON, FromJSON) (MyType 'Foo)
```
Should this proposal get rejected, I think we should at least document this limitation.