关注this tutorial,我有以下代码:
{-# LANGUAGE DataKinds, TypeFamilies #-}
data Nat = Z | S Nat
type family Plus (n :: Nat) (m :: Nat) :: Nat
type instance Plus Z m = m
type instance Plus (S n) m = S (Plus n m)
到目前为止一切顺利。我知道DataKinds
会自动将类型提升为种类,TypeFamilies
会启用类型级函数。
然后我补充说:
type family Mul (m :: Nat) (n :: Nat) :: Nat
type instance Mul Z m = Z
type instance Mul (S n) m = Plus m (Mul n m)
编译给了我:
Nested type family application
in the type family application: Plus m (Mul n m)
(Use UndecidableInstances to permit this)
In the type instance declaration for ‘Mul’
此扩展程序的名称很吓人。有些人对avoid说,并且我尝试阅读的这个扩展的解释对我来说没有意义。
在理解错误时寻找一些帮助,为什么它在这个例子中出现,在这个上下文中 undecidable 的含义是什么,在什么情况下这个扩展像它一样可怕声音?
答案 0 :(得分:2)
这并不可怕。它禁用(相当弱)GHC检查,试图确定类型族和实例定义终止。因此,其最坏的情况是非终止类型检查。但是,大多数情况下我们会收到错误消息而不是实际循环,因为循环通常会超出类型检查器中的堆栈限制。我们不会得到不安全,不连贯或任何其他可能对程序稳健性或推理产生负面影响的财产。
“Undecidable”可能是一个比保证更强的表达,因为它只表示GHC无法决定定义是否终止。在许多情况下,GHC对于工作来说太弱了,而其他依赖语言的编译器也能够毫无问题地检查终止。对于GHC,UndecidableInstances
通常也需要显式终止定义,在这种情况下,它的使用是非常合理的。
答案 1 :(得分:2)
UndecidableInstance
可以使用。当然,你应该总是考虑片刻是否确实需要某些东西,或者选择另一条道路更为惯用。
Luke指出UndecidableInstances
不应该用于实现超类。嗯,这是真的,但它与不可判断的实例并没有多大关系 - 超类只是一个黑客,而且几乎与设计抽象的方式相反。
IME 不是真的,至少不再是,超级类是UndecidableInstances
最常见的用法。启用此扩展程序可以做很多事情但不能没有。当然,如果你实际上希望在编译时进行图灵完全相关的打字,那么不能不能强加防止停止的条件,这基本上是什么所有DataKinds模糊都要前往。
所以,不要太担心;事实上,你可能不会绕过UndecidableInstances
。正如AndrásKovács所说,这个扩展是完全安全的WRT 正确性,可能发生的最坏情况是循环/错误。
如果这对您的口味太可怕了,请使用前面设计的语言作为依赖类型和总数,即Agda。