Created
November 20, 2024 19:03
-
-
Save Lysxia/2d963b5ea0f953fd18d35ce51af8c4e4 to your computer and use it in GitHub Desktop.
Transferring generic deriving to custom classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env cabal | |
{- cabal: | |
build-depends: | |
base, | |
generic-data | |
-} | |
{-# LANGUAGE ScopedTypeVariables, TypeApplications, DeriveGeneric, FlexibleContexts, UndecidableInstances, ConstraintKinds, DerivingVia #-} | |
import GHC.Generics (Rep, Generic) | |
import Data.Coerce (Coercible, coerce) | |
import Generic.Data | |
import Generic.Data.Microsurgery | |
{- Goal | |
Derive custom equality function generically: | |
1. don't generate Eq instances, | |
2. but reuse pre-existing generic deriving machinery for Eq. | |
-} | |
{- Solution: define custom MyEq class that will support deriving via Generically | |
by relying on generic deriving for Eq. | |
-} | |
class MyEq a where | |
myEq :: a -> a -> Bool | |
data MyT1 | |
= MyT1 MyT2 | |
deriving Generic | |
deriving MyEq via Generically MyT1 | |
data MyT2 | |
= MyT2 Int | |
deriving Generic | |
deriving MyEq via Generically MyT2 | |
main :: IO () | |
main = print (MyT1 (MyT2 0) `myEq` MyT1 (MyT2 0)) | |
-- Implementation | |
-- 1. Build on top of generic deriving exported from Generic.Data as geq. | |
-- 2. Define a newtype UsingMyEq to implement Eq using MyEq. We use this newtype to wrap fields of generic data types, via "microsurgeries": | |
-- instead of applying `geq` to the generic type `a`, we apply it to `Surgery' (OnFields UsingMyEq) a`. | |
newtype UsingMyEq a = UsingMyEq a | |
instance MyEq a => Eq (UsingMyEq a) where | |
UsingMyEq x == UsingMyEq y = myEq x y | |
type EqVia x y = (Eq (x ()), Coercible x y) | |
instance (Generic a, EqVia (GSurgery (OnFields UsingMyEq) (Rep a)) (Rep a)) => MyEq (Generically a) where | |
myEq = coerce (geq @(Surgery' (OnFields UsingMyEq) a)) | |
-- 3. Define instances for MyEq for base types. | |
instance MyEq Int where | |
myEq = (==) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment