【发布时间】:2022-01-08 09:00:12
【问题描述】:
我想要一个类型类来告诉我各种类型有多大。
data Cardinality = Finite Natural | Infinite
class Sized a where cardinality :: Cardinality
编写实例非常简单;例如:
instance Sized Void where cardinality = Finite 0
instance Sized () where cardinality = Finite 1
instance Sized Bool where cardinality = Finite 2
instance Sized a => Sized [a] where
cardinality = case cardinality @a of
Finite 0 -> Finite 1
_ -> Infinite
data X = X Y
data Y = Y X X
instance Sized X where cardinality = Finite 1
instance Sized Y where cardinality = Finite 1
事实上,它是如此简单,感觉它应该是可自动化的。也许泛型编程会有所帮助?
class GSized f where gcardinality :: Cardinality
class Sized a where
cardinality :: Cardinality
default cardinality :: (Generic a, GSized (Rep a)) => Cardinality
cardinality = gcardinality @(Rep a)
大多数实例都非常简单:
instance GSized V1 where gcardinality = Finite 0
instance GSized U1 where gcardinality = Finite 1
instance GSized f => GSized (M1 i c f) where gcardinality = gcardinality @f
instance (GSized f, GSized g) => GSized (f :+: g) where
gcardinality = case (gcardinality @f, gcardinality @g) of
(Finite n, Finite n') -> Finite (n+n')
_ -> Infinite
instance (GSized f, GSized g) => GSized (f :*: g) where
gcardinality = case (gcardinality @f, gcardinality @g) of
(Finite 0, _) -> Finite 0
(_, Finite 0) -> Finite 0
(Finite n, Finite n') -> Finite (n*n')
_ -> Infinite
但后来我卡住了。单个字段的直接操作肯定行不通:
instance Sized c => GSized (K1 i c) where
gcardinality = cardinality @c
对于递归类型,这是一个非常简单的无限循环。我尝试了多种方法来丰富这里涉及的两个类。
- 我将
cardinality和gcardinality概括为函数,因此我可以假设我已经知道递归事件的大小。然后在K1实例中我可以问:如果你所有的递归实例都无人居住,你会有多大?如果所有递归实例都有一个居民怎么办?等等。 - 我从单一基数概括为像
x = a + bx这样的“递归关系”。x = a + bx的预期含义是x类型具有完全不涉及递归的a居民,以及可以与递归调用配对的b值。 (我们可以定义x*x = x而不会失去任何有趣的东西。)然后像x = n + 0x这样的方程对应于枚举,x = 0 + x对应于平凡的 1 元素递归类型,x = a + bx是无限的。 - 我使用了懒惰的自然而不是高效的。
- 我跟踪了一组可能的基数,并在了解新信息后将它们一一排除。
- 我尝试使用
Generic1而不是Generic。
这些都没有成功。似乎存在三种核心的难题/失败模式:
-
K1定义中用于递归类型的循环,或者对于递归类型可以正常工作,但对于相互递归类型会失败。 -
K1用于递归出现和普通字段,似乎没有简单的方法来区分它们。 - 为普通递归类型选择
Finite 0而不是Finite 1。
是否有解决这些问题的方法?上面的[Void]、[()]、X 和Y 的通用实例在哪里都已定义且正确?
【问题讨论】:
-
这不是类似于检测像
fix (1:)这样的循环数据结构吗——是否存在类型级别的可用工具,而不是术语级别的工具可以实现这一点? -
如果你计算底部,那么懒惰的自然方法应该有效。也许你可以计算包括底部在内的所有值,只计算底部,然后减去它们,你会得到正确的有限情况。如果任一类型的值都无限多,则该类型是无限的(因此您所要做的就是检测惰性自然是否是无限的——也许您可以在类型级别做到这一点(我孩子))
-
但是,是的... ISTM 本质上将其具体化为图形是获得您想要的
Finite | Infinite答案的唯一方法——所以拿出TypeRep大锤,用粗心的方式去做.你试过这个吗?