如何创建一个;“善良的阶级”;在Haskell中,或使用类型族在类型级别上进行特殊多态性
我正在研究Haskell的类型族特征和类型级别计算。 使用如何创建一个;“善良的阶级”;在Haskell中,或使用类型族在类型级别上进行特殊多态性,haskell,types,typeclass,type-families,data-kinds,Haskell,Types,Typeclass,Type Families,Data Kinds,我正在研究Haskell的类型族特征和类型级别计算。 使用polytypes,似乎很容易在类型级别获得参数多态性: {-# LANGUAGE DataKinds, TypeFamilies, KindSignatures, GADTs, TypeOperators, UndecidableInstances, PolyKinds, MultiParamTypeClasses, FlexibleInstances #-} data NatK = Z | S NatK data IntK = I
polytypes
,似乎很容易在类型级别获得参数多态性:
{-# 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
我可以做一些事情,比如:善良!Bool==Bool
或:种类!Int==Int
或:种类!Z==Z
和:种类!(I Z(S Z))==(I(S Z)(S(S Z)))
但是我想使type+
ad-hoc多态。所以它被限制在我给它的实例中。这里的两个实例是kindNatK
类型和kindIntK
类型
我首先尝试使其参数多态:
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)
这很管用,就像我能做的那样:善良!(I(sz)Z):+(I(sz)Z)
不过我也可以做:善良!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)
它仍然允许:种类!添加布尔布尔布尔
这是否与ConstraintKinds
扩展有关,我需要将:+
或添加到某个“种类类”?(这应该是一个注释,但我需要更多空间)
我试过类似的东西
class GoodK (Proxy k) => NumK (a :: k) (b :: k) where ...
但我失败了。我不知道你的要求是否可以实现
我得到的最佳近似值是进行addbool-Bool
kind检查,但生成一个无法解决的约束,这样,如果我们使用它,我们无论如何都会得到一个错误。
也许这对于你的目的来说已经足够了(?)
最简单的解决方案是使用开放类型族进行特殊重载,使用封闭类型族进行实现:
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
如果我们希望将多个类型级和术语级方法组合在一起,我们可以使用KProxy
从以下位置编写种类类:
当然,关联类型与开放类型族相同,因此我们也可以将开放类型族与单独的类一起用于术语级方法。但我认为在同一个类中使用所有重载名称通常更干净
从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
谢谢这是很酷的,但你能详细说明到底是什么让它工作的吗?也就是说,它为什么起作用?对于您的开放+封闭解决方案、KProxy解决方案和TypeInType解决方案。哦,但我刚刚测试了您的第一个解决方案,它仍然允许:kind!添加Bool Bool
导致添加Bool Bool::*
。我希望这会成为一个类型错误,而不是被接受!?另外,您的第二个解决方案还允许添加Bool Bool
。它不会显示为类型错误。这是不可避免的。如果我们有一个类型良好的家族应用程序,并且参数没有匹配的情况,那么我们会得到一个卡住的应用程序,而不是运行时的模式错误。卡住的应用程序不能用于任何事情(带有),因此此行为是安全的。因此,定义单一多边形封闭类型族的解决方案也是安全的,只是不可扩展/模块化。
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
{-# 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