我只阅读了标准教程并摸索了一下,所以我可能错过了一些简单的东西。
如果这在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
Nat
是Ord
的一个实例,那么问题是什么?我尝试用Bool
(不是Ord
的实例)替换v''
中的Nat
s,但错误没有变化。
另一个角度…
使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
我可能错过了一些关于如何根据类型声明检查值的关键见解。
正如其他答案所解释的,这是关于如何以及在哪里绑定变量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
当你调用emptyListOfOrds
时,你可以使用特殊的语法%instance
来搜索默认实例:
v'' = emptyListOfOrds Nat %instance
当然,您并不想在每个调用站点都这样做,因此您可以使用默认的隐式实参来为您调用搜索过程:
emptyListOfOrds : (t : Type) -> {default %instance x : Ord t} -> List t
v'' = emptyListOfOrds Nat
如果没有显式给出其他值,default val x : T
语法将用默认值val
填充隐式参数x
。将%instance
作为默认值,那么与类约束发生的情况几乎相同,实际上我们可能会改变Foo x =>
语法的实现来做到这一点……我认为我没有这样做的唯一原因是default
参数还不存在,当我实现类型类的第一。
你可以写
emptyListOfOrds : Ord t => List t
emptyListOfOrds = []
v'' : List Nat
v'' = emptyListOfOrds
或者如果你喜欢
v'' = emptyListOfOrds {t = Nat}
如果您按照您所写的方式请求emptylistfords的类型,您将得到
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?也许与显式类型参数和类型类约束有一些冲突?通常,类型类用于隐式解决问题时…
不是答案,只是一些想法。
这里的问题是(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
参数隐式