Idris函数构造空`List a`,其中`a`绑定到`Ord`的实例?

时间:2014-07-19 06:20:12

标签: typechecking idris

我只阅读了standard tutorial并且稍微摸了一下,所以我可能会遗漏一些简单的东西。

如果Idris无法做到这一点,请解释原因。此外,如果可以用其他语言完成,请提供代码示例,并解释该语言类型系统的不同之处。

这是我的方法。问题首先出现在第三部分。

创建一个已知类型的空列表

 v : List Nat
 v = []

这在REPL中编译并显示为[] : List Nat。优异。

推广到任何提供的类型

 emptyList : (t : Type) -> List t
 emptyList t = []

 v' : List Nat
 v' = emptyList Nat

不出所料,这可行,v' == v

将类型约束为Ord

的实例
emptyListOfOrds : Ord t => (t : Type) -> List t
emptyListOfOrds t = []

v'' : List Nat
v'' = emptyListOfOrds Nat     -- !!! typecheck failure

最后一行因此错误而失败:

When elaborating right hand side of v'':
Can't resolve type class Ord t

NatOrd的一个实例,那么问题是什么?我尝试将Nat中的v''替换为Bool(不是Ord的实例),但错误没有变化。

另一个角度......

使Ord t显式参数满足类型检查器吗?显然不是,但即使它确实要求呼叫者传递冗余信息也不是理想的。

 emptyListOfOrds' : Ord t -> (t : Type) -> List t
 emptyListOfOrds' a b = []

 v''' : List Nat
 v''' = emptyListOfOrds (Ord Nat) Nat     -- !!! typecheck failure

这次错误更详细:

 When elaborating right hand side of v''':
 When elaborating an application of function stackoverflow.emptyListOfOrds':
         Can't unify
                 Type
         with
                 Ord t

         Specifically:
                 Can't unify
                         Type
                 with
                         Ord t

我可能错过了一些关于如何根据类型声明检查值的关键见解。

3 个答案:

答案 0 :(得分:8)

正如其他答案所解释的那样,这是关于变量t的约束方式和位置。也就是说,当你写:

emptyListOfOrds : Ord t => (t : Type) -> List t

阐述者会看到' t'在Ord t中使用它时未绑定,因此隐式绑定它:

emptyListOfOrds : {t : Type} -> Ord t => (t : Type) -> List t

所以你真正想说的是有点像:

emptyListOfOrds : (t : Type) -> Ord t => List t

哪个会在类型类约束之前绑定t,因此当Ord t出现时它就在范围内。不幸的是,这种语法不受支持。我认为没有理由不支持这种语法,但目前还没有。

你仍然可以实现你想要的东西,但它很难看,我很害怕:

由于类是第一类,因此可以将它们作为普通参数提供:

emptyListOfOrds : (t : type) -> Ord t -> List t

然后,当您致电%instance时,您可以使用特殊语法emptyListOfOrds搜索默认实例:

v'' = emptyListOfOrds Nat %instance

当然,您并不是真的想在每个呼叫站点都这样做,因此您可以使用默认的隐式参数来为您调用搜索过程:

emptyListOfOrds : (t : Type) -> {default %instance x : Ord t} -> List t
v'' = emptyListOfOrds Nat

如果没有明确给出其他值,default val x : T语法将使用默认值x填充隐式参数val。将%instance作为默认值然后与类约束发生的情况完全相同,实际上我们可能会改变Foo x =>语法的实现来完成这个......我认为这是我唯一的原因当我最初实现类型类时,default个参数还没有存在。

答案 1 :(得分:2)

你可以写

emptyListOfOrds : Ord t => List t
emptyListOfOrds = []

v'' : List Nat
v'' = emptyListOfOrds

或许如果你愿意

 v'' = emptyListOfOrds {t = Nat}

如果你按照你写的方式询问emptyListOfOrds的类型,你得到

Ord t => (t2 : Type) -> List t2

在repl中:set showimplicits上图示,然后再次询问

{t : Type} -> Prelude.Classes.Ord t => (t2 : Type) -> Prelude.List.List t2

似乎指定Ord t约束引入了隐式参数t,然后为您的显式参数t分配了新名称。您始终可以为该隐式参数显式提供值,例如emptyListOfOrds {t = Nat} Nat。至于这是"权利"出于某种原因的行为或限制,也许你可以在github上打开这个问题?也许与显式类型参数和类型类约束存在一些冲突?通常情况下,类型类是在你隐式解决的时候...虽然我想我记得有语法来获得对类型类实例的显式引用。

答案 2 :(得分:0)

不是答案,只是一些想法。

这里的问题是(t : Type)引入了扩展到右边的新范围,但Ord t超出了此范围:

*> :t emptyListOfOrds 
emptyListOfOrds : Ord t => (t2 : Type) -> List t2

您可以在引入类型变量后添加类约束:

emptyListOfOrds : (t : Type) -> Ord t -> List t
emptyListOfOrds t o = []

但现在你需要明确指定类实例:

instance [natord] Ord Nat where
  compare x y = compare x y

v'' : List Nat
v'' = emptyListOfOrds Nat @{natord}

可能以某种方式使Ord t参数隐含。