Coercible Bool Mark
不需要。Mark
-instances 可以通过Bool
没有它派生。
Generic
泛型表示 ( Rep
) 的类型Coercible
可以相互转换:
from coerce to
A -----> Rep A () -----> Rep Via () -----> Via
对于数据类型,Mark
这意味着实例 ( Eq
, ..) 可以通过Bool
.
type Mark :: Type
data Mark = Nought | Cross
deriving
stock Generic
deriving Eq
via Bool <-> Mark
如何Bool <-> Mark
工作?
type (<->) :: Type -> Type -> Type
newtype via <-> a = Via a
首先,我们捕获我们可以coerce
在两种类型的通用表示之间的约束:
type CoercibleRep :: Type -> Type -> Constraint
type CoercibleRep via a = (Generic via, Generic a, Rep a () `Coercible` Rep via ())
鉴于此约束,我们可以从a
to itvia
类型移动,创建中间Rep
s:
translateTo :: forall b a. CoercibleRep a b => a -> b
translateTo = from @a @() >>> coerce >>> to @b @()
现在我们可以很容易地Eq
为这种类型编写一个实例,我们假设一个Eq via
via 类型的实例(Bool
在我们的例子中)
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
的实例Semigroup
需要翻译via
回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
现在我们可以推导出Eq
和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
从一开始就使用 anewtype
可以避免这种样板,但一旦它启动,它就可以被重用。编写一个新类型并使用模式同义词来掩盖它很简单。