Coercible Bool Mark
is not required. Mark
-instances can be derived via Bool
without it.
Generic
types whose generic representations (Rep
) are Coercible
can be converted to each other:
from coerce to
A -----> Rep A () -----> Rep Via () -----> Via
For the datatype Mark
this means instances (Eq
, ..) can be derived via instances of Bool
.
type Mark :: Type
data Mark = Nought | Cross
deriving
stock Generic
deriving Eq
via Bool <-> Mark
How does Bool <-> Mark
work?
type (<->) :: Type -> Type -> Type
newtype via <-> a = Via a
First we capture the constraint that we can coerce
between the generic representation of two types:
type CoercibleRep :: Type -> Type -> Constraint
type CoercibleRep via a = (Generic via, Generic a, Rep a () `Coercible` Rep via ())
Given this constraint we can move from a
to it via
type, creating intermediate Rep
s:
translateTo :: forall b a. CoercibleRep a b => a -> b
translateTo = from @a @() >>> coerce >>> to @b @()
Now we can easily write an Eq
instance for this type, we assume an Eq via
instance for the via type (Bool
in our case)
instance (CoercibleRep via a, Eq via) => Eq (via <-> a) where
(==) :: (via <-> a) -> (via <-> a) -> Bool
Via a1 == Via a2 = translateTo @via a1 == translateTo @via a2
The instance for Semigroup
requires translating via
back to a
instance (CoercibleRep via a, Semigroup via) => Semigroup (via <-> a) where
(<>) :: (via <-> a) -> (via <-> a) -> (via <-> a)
Via a1 <> Via a2 = Via do
translateTo @a do
translateTo @via a1 <> translateTo @via a2
Now we can derive Eq
and Semigroup
!
-- >> V3 "a" "b" "c" <> V3 "!" "!" "!"
-- V3 "a!" "b!" "c!"
type V4 :: Type -> Type
data V4 a = V4 a a a a
deriving
stock Generic
deriving (Eq, Semigroup)
via (a, a, a, a) <-> V4 a
Using a newtype
from the beginning avoids this boilerplate but once it's up it can be reused. It is simple to write a newtype and use pattern synonyms to cover it up.
unsafeCoerce
, which will probably work but which would not be recommended. The only way to get aCoercible
instance would be local, usingunsafeCoerce
.case unsafeCoerce (Coercion @() @()) :: Coercible Mark Bool of Coercion -> ....
. I wouldn't really suggest it unless there's a desperate performance need. – Genevagenevan