如何在 Haskell 中创建 "kind class",或使用类型族在类型级别创建临时多态性
How to create a "kind class" in Haskell, or ad-hoc polymorphism at the type-level using type families
我正在研究 Haskell 的类型族特征和类型级计算。
使用 PolyKinds
:
似乎很容易在类型级别获得参数多态性
{-# LANGUAGE DataKinds, TypeFamilies, KindSignatures, GADTs, TypeOperators, UndecidableInstances, PolyKinds, MultiParamTypeClasses, FlexibleInstances #-}
data NatK = Z | S NatK
data IntK = I NatK NatK
infix 6 +
type family (x :: NatK) + (y :: NatK) :: NatK where
Z + y = y
(S x) + y = S (x + y)
-- here's a parametrically polymorphic (==) at the type-level
-- it also deals specifically with the I type of kind IntK
infix 4 ==
type family (a :: k) == (b :: k) :: Bool where
(I a1 a2) == (I b1 b2) = (a1 + b2) == (a2 + b1)
a == a = True
a == b = False
我可以做 :kind! Bool == Bool
或 :kind! Int == Int
或 :kind! Z == Z
和 :kind! (I Z (S Z)) == (I (S Z) (S (S Z)))
。
不过我想让 type +
临时多态。所以它受限于我给它的实例。这里的 2 个实例是类型 NatK
和类型 IntK
.
我首先尝试使其参数化多态:
infix 6 :+
type family (x :: k) :+ (y :: k) :: k where
Z :+ y = y
(S x) :+ y = S (x :+ y)
(I x1 x2) :+ (I y1 y2) = I (x1 :+ y1) (x2 :+ y2)
这行得通,我也能做到:kind! (I (S Z) Z) :+ (I (S Z) Z)
。
不过我也可以:kind! Bool :+ Bool
。这没有任何意义,但它允许它作为一个简单的类型构造函数。我想创建一个不允许此类错误类型的类型系列。
此时我迷路了。我尝试使用 type
参数键入 类。但这没有用。
class NumK (a :: k) (b :: k) where
type Add a b :: k
instance NumK (Z :: NatK) (b :: NatK) where
type Add Z b = b
instance NumK (S a :: NatK) (b :: NatK) where
type Add (S a) b = S (Add a b)
instance NumK (I a1 a2 :: IntK) (I b1 b2 :: IntK) where
type Add (I a1 a2) (I b1 b2) = I (Add a1 b1) (Add a2 b2)
它仍然允许:kind! Add Bool Bool
。
这是否与 ConstraintKinds
扩展有关,我需要将 :+
或 Add
限制为某些 "kind class"?
(这应该是评论,但我需要更多 space)
我试过
class GoodK (Proxy k) => NumK (a :: k) (b :: k) where ...
但是我失败了。我不知道你的要求是否可以实现。
我得到的最佳近似是进行 Add Bool Bool
类检查,但会生成无法解决的约束,因此如果我们使用它,无论如何都会出错。
也许这足以满足您的目的 (?)。
class Fail a where
instance Fail a => NumK (a :: *) (b :: *) where
type Add a b = ()
最简单的解决方案是使用开放类型族进行临时重载,使用封闭类型族进行实现:
data NatK = Z | S NatK
data IntK = I NatK NatK
type family Add (x :: k) (y :: k) :: k
type family AddNatK (a :: NatK) (b :: NatK) where
AddNatK Z b = b
AddNatK (S a) b = S (AddNatK a b)
type family AddIntK (a :: IntK) (b :: IntK) where
AddIntK (I a b) (I a' b') = I (AddNatK a a') (AddNatK b b')
type instance Add (a :: NatK) (b :: NatK) = AddNatK a b
type instance Add (a :: IntK) (b :: IntK) = AddIntK a b
如果我们想要将多个类型级别和术语级别的方法组合在一起,我们可以使用 using KProxy
from Data.Proxy
:
编写 kind classes
class NumKind (kproxy :: KProxy k) where
type Add (a :: k) (b :: k) :: k
-- possibly other methods on type or term level
instance NumKind ('KProxy :: KProxy NatK) where
type Add a b = AddNatK a b
instance NumKind ('KProxy :: KProxy IntK) where
type Add a b = AddIntK a b
当然,关联类型与开放类型家族相同,因此我们也可以使用开放类型家族,并使用单独的 class 作为术语级方法。但我认为将所有重载名称放在同一个 class.
中通常更干净
从 GHC 8.0 开始,KProxy
变得不必要,因为种类和类型将以完全相同的方式处理:
{-# LANGUAGE TypeInType #-}
import Data.Kind (Type)
class NumKind (k :: Type) where
type Add (a :: k) (b :: k) :: k
instance NumKind NatK where
type Add a b = AddNatK a b
instance NumKind IntK where
type Add a b = AddIntK a b
我正在研究 Haskell 的类型族特征和类型级计算。
使用 PolyKinds
:
{-# LANGUAGE DataKinds, TypeFamilies, KindSignatures, GADTs, TypeOperators, UndecidableInstances, PolyKinds, MultiParamTypeClasses, FlexibleInstances #-}
data NatK = Z | S NatK
data IntK = I NatK NatK
infix 6 +
type family (x :: NatK) + (y :: NatK) :: NatK where
Z + y = y
(S x) + y = S (x + y)
-- here's a parametrically polymorphic (==) at the type-level
-- it also deals specifically with the I type of kind IntK
infix 4 ==
type family (a :: k) == (b :: k) :: Bool where
(I a1 a2) == (I b1 b2) = (a1 + b2) == (a2 + b1)
a == a = True
a == b = False
我可以做 :kind! Bool == Bool
或 :kind! Int == Int
或 :kind! Z == Z
和 :kind! (I Z (S Z)) == (I (S Z) (S (S Z)))
。
不过我想让 type +
临时多态。所以它受限于我给它的实例。这里的 2 个实例是类型 NatK
和类型 IntK
.
我首先尝试使其参数化多态:
infix 6 :+
type family (x :: k) :+ (y :: k) :: k where
Z :+ y = y
(S x) :+ y = S (x :+ y)
(I x1 x2) :+ (I y1 y2) = I (x1 :+ y1) (x2 :+ y2)
这行得通,我也能做到:kind! (I (S Z) Z) :+ (I (S Z) Z)
。
不过我也可以:kind! Bool :+ Bool
。这没有任何意义,但它允许它作为一个简单的类型构造函数。我想创建一个不允许此类错误类型的类型系列。
此时我迷路了。我尝试使用 type
参数键入 类。但这没有用。
class NumK (a :: k) (b :: k) where
type Add a b :: k
instance NumK (Z :: NatK) (b :: NatK) where
type Add Z b = b
instance NumK (S a :: NatK) (b :: NatK) where
type Add (S a) b = S (Add a b)
instance NumK (I a1 a2 :: IntK) (I b1 b2 :: IntK) where
type Add (I a1 a2) (I b1 b2) = I (Add a1 b1) (Add a2 b2)
它仍然允许:kind! Add Bool Bool
。
这是否与 ConstraintKinds
扩展有关,我需要将 :+
或 Add
限制为某些 "kind class"?
(这应该是评论,但我需要更多 space)
我试过
class GoodK (Proxy k) => NumK (a :: k) (b :: k) where ...
但是我失败了。我不知道你的要求是否可以实现。
我得到的最佳近似是进行 Add Bool Bool
类检查,但会生成无法解决的约束,因此如果我们使用它,无论如何都会出错。
也许这足以满足您的目的 (?)。
class Fail a where
instance Fail a => NumK (a :: *) (b :: *) where
type Add a b = ()
最简单的解决方案是使用开放类型族进行临时重载,使用封闭类型族进行实现:
data NatK = Z | S NatK
data IntK = I NatK NatK
type family Add (x :: k) (y :: k) :: k
type family AddNatK (a :: NatK) (b :: NatK) where
AddNatK Z b = b
AddNatK (S a) b = S (AddNatK a b)
type family AddIntK (a :: IntK) (b :: IntK) where
AddIntK (I a b) (I a' b') = I (AddNatK a a') (AddNatK b b')
type instance Add (a :: NatK) (b :: NatK) = AddNatK a b
type instance Add (a :: IntK) (b :: IntK) = AddIntK a b
如果我们想要将多个类型级别和术语级别的方法组合在一起,我们可以使用 using KProxy
from Data.Proxy
:
class NumKind (kproxy :: KProxy k) where
type Add (a :: k) (b :: k) :: k
-- possibly other methods on type or term level
instance NumKind ('KProxy :: KProxy NatK) where
type Add a b = AddNatK a b
instance NumKind ('KProxy :: KProxy IntK) where
type Add a b = AddIntK a b
当然,关联类型与开放类型家族相同,因此我们也可以使用开放类型家族,并使用单独的 class 作为术语级方法。但我认为将所有重载名称放在同一个 class.
中通常更干净从 GHC 8.0 开始,KProxy
变得不必要,因为种类和类型将以完全相同的方式处理:
{-# LANGUAGE TypeInType #-}
import Data.Kind (Type)
class NumKind (k :: Type) where
type Add (a :: k) (b :: k) :: k
instance NumKind NatK where
type Add a b = AddNatK a b
instance NumKind IntK where
type Add a b = AddIntK a b