Deriving Strategies, Generics and Coercible
Roles (type role, nominal/representational/phantom) control where coerce is allowed, which keeps invariants (e.g., Map k v's key ordering) safe.
{-# LANGUAGE DerivingStrategies, DerivingVia, DeriveAnyClass, GeneralizedNewtypeDeriving, DeriveGeneric #-}
import GHC.Generics (Generic)
import Data.Monoid (Sum (..))
import Data.Aeson (ToJSON, FromJSON)
newtype Age = Age Int
deriving stock (Show) -- standard deriving
deriving newtype (Eq, Ord, Num) -- reuse Int's instances (GND)
newtype Score = Score Int
deriving (Semigroup, Monoid) via (Sum Int) -- DerivingVia: borrow another type's instance
data User = User {userName :: String, userAge :: Int}
deriving stock (Show, Eq, Generic)
deriving anyclass (ToJSON, FromJSON) -- empty instances using Generic defaults
-- Standalone deriving
data Point = Point Int Int
deriving stock instance Show Point
-- Coercible: zero-cost conversion between representationally equal types
import Data.Coerce (coerce)
ages :: [Age]
ages = coerce [30, 25 :: Int] -- no runtime traversal
-- GHC.Generics: write functions generic over the structure of data types
-- (used by aeson, binary, hashable, QuickCheck's generic-arbitrary, deepseq...)