Skip to content

Instantly share code, notes, and snippets.

@Lysxia
Created November 20, 2024 19:03
Show Gist options
  • Save Lysxia/2d963b5ea0f953fd18d35ce51af8c4e4 to your computer and use it in GitHub Desktop.
Save Lysxia/2d963b5ea0f953fd18d35ce51af8c4e4 to your computer and use it in GitHub Desktop.
Transferring generic deriving to custom classes
#!/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