如何创建一个;“善良的阶级”;在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

我正在研究Haskell的类型族特征和类型级别计算。 使用
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多态。所以它被限制在我给它的实例中。这里的两个实例是kind
NatK
类型和kind
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)
这很管用,就像我能做的那样
:善良!(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