Types 依赖类型能否在n-arg函数上抽象?

Types 依赖类型能否在n-arg函数上抽象?,types,dynamic-typing,static-typing,dependent-type,Types,Dynamic Typing,Static Typing,Dependent Type,在动态类型语言中,我可以创建一个函数,该函数将函数作为参数并返回函数 例如,Clojure中的memoize函数 (def memoized-fn (memoize any-function)) 在本例中,memoize不关心任何函数所引用的函数,也不关心它接受多少个参数* *实际上,它并不关心传入的内容,(memoize 10)是有效的Clojure,但尝试使用返回值只会引发异常 在以前的生活中,我想用静态类型语言创建类似的东西,在我的例子中,我使用Scala,Scala有许多函数n

在动态类型语言中,我可以创建一个函数,该函数将函数作为参数并返回函数

例如,Clojure中的
memoize
函数

(def memoized-fn 
  (memoize any-function))
在本例中,
memoize
不关心任何函数所引用的函数,也不关心它接受多少个参数*

*实际上,它并不关心传入的内容,
(memoize 10)
是有效的Clojure,但尝试使用返回值只会引发异常


在以前的生活中,我想用静态类型语言创建类似的东西,在我的例子中,我使用Scala,Scala有许多
函数n
类型(我相信1 arg到23 arg),但是,如果函数之间没有任何关系,似乎就无法利用它们的函数性来创建单个泛型函数

最后我得到了这样的东西*

def m(fn: Function1[A,Z]) : Function1[A,Z]
def m(fn: Function2[A,B,Z]) : Function2[A,B,Z]
....
def m(fn: Function23[A,B,....,Z]) : (fn: Function23[A,B,....,Z])
(实际上,我在FN4或FN5附近停了下来,因为虽然我很高兴Function23能够存在,但我从来不想实际使用它。)

*这可能也是psuedo-Scala代码,我已经有一段时间没有写过了


回到今天:我了解到,使用依赖类型,我可以创建一个函数,该函数接受一个用值参数化的参数。这方面的一个老生常谈的例子似乎是一个函数,它接受任何类型和大小的列表n,并返回一个大小相同的列表n

我可以理解同质列表(一种大小为n的a类列表),但我还不知道异质列表是否有这种可能性

由此我假设我可以创建一个函数,它接受n个相同类型的参数。大致如下:

def m(fn: Function[n,A]): Function[n,A]

我想我的实际问题是:依赖值能否以异构的方式影响类型的数量

另请注意:请将上述备忘录示例和语言仅作为我想法的示例,我不是问如何使用静态类型语言进行备忘录,而是问一个更高层次的问题,以备忘录代码为例

*请原谅我在这个问题上缺乏更好的词汇,我仍然在学习很多。也欢迎提出编辑/改进建议。

依赖类型(PI)基本上是功能空间的“强化”形式(箭头)

如果你看到:

Pi x : T. T(x)
然后它的类型基本上是说“给我一个x类型的
T
,我会给你一些
T(x)

在退化情况下,
T
的主体中没有任何
x
的实例,这相当于其他语言中的
T1->T2
(用
T1
替换上面
x
中的东西)。对你的问题的简单回答是,不,你通常不能增加一个类型中的“箭头数”,但是你可以做一些基本上是这样的事情,如果你有这个

Pi x : nat. listoflength(x)
其中,
listoflength
是生成长度为
x
的列表(某种类型,可能是另一个参数)。例如,您可以使用sigma类型来实现它(例如,以长度作为其参数的列表)。问题是参数必须是同质类型。另一种方法是创建一个函数,给定一个参数,该函数将返回一个包含
n
事物的元组,每个事物都有某种类型

基本的问题是,如果你给出一个特定的
n
,那么你需要能够说,“我接受
n
参数,这里是它们的类型”,以使所有类型的算术运算都有效

因此,虽然不能创建任意数量的箭头,但可以创建一些生成一个以元组作为输入的函数的东西(我想如果您知道
n
,可以在使用它的地方取消对它的修剪)


在实际的依赖类型编程中,我不知道这会有多大用处(我没有证据表明它没有用处),但我可以看到函数采用特定大小的列表的情况,其中大小必须有一些静态计算来证明它们“匹配”。在要接受异构输入类型的情况下,在实践中对此进行推理可能会很复杂,我通常会在编码实践中尽量避免这种情况。在动态语言中,你可以有不同数量的参数,看起来用例可能是让你在道德上很容易地“重载”一个函数,但是在依赖类型的语言中,似乎您从中获得的任何符号便利都将丢失,因为您必须证明您正确地使用了实例。

如果您知道arity-generic数据类型通用编程论文中描述的技巧,那么编写异构arity-generic函数很容易。首先,您创建一个函数,它接收类型的向量,然后,您“curry”这个函数,以便它接收隐式类型而不是显式向量

首先是一些进口产品:

open import Data.Nat
open import Data.Vec
open import Data.Vec.N-ary
本文中有两个组合词:

∀⇒ : ∀ {n α β} {A : Set α} 
   -> (Vec A n -> Set β) 
   -> Set (N-ary-level α β n)
∀⇒ {0}     B = B []
∀⇒ {suc n} B = ∀ {x} -> ∀⇒ (λ xs -> B (x ∷ xs))

λ⇒ : ∀ {n α β} {A : Set α} {B : Vec A n -> Set β} 
   -> ((xs : Vec A n) -> B xs) 
   -> ∀⇒ B
λ⇒ {0}     f = f []
λ⇒ {suc n} f = λ {x} -> λ⇒ (λ xs -> f (x ∷ xs))
以及依赖多态性arity泛型组合函数,该函数接收类型的显式向量

Vec-ary : ∀ {α γ l} -> Vec (Set α) l -> Set γ -> Set (N-ary-level α γ l)
Vec-ary  []      Z = Z
Vec-ary (X ∷ Xs) Z = X -> Vec-ary Xs Z

compT : ∀ {α β γ l} {Y : Set β} 
         -> (Xs : Vec (Set α) l)
         -> Vec-ary Xs Y
         -> (Y -> Set γ)
         -> Set (N-ary-level α γ l)
compT  []      y Z = Z y
compT (X ∷ Xs) g Z = (x : X) -> compT Xs (g x) Z

comp' : ∀ {α β γ l} {Y : Set β} {Z : Y -> Set γ}
      -> (Xs : Vec (Set α) l)
      -> (f : (y : Y) -> Z y)
      -> (g : Vec-ary Xs Y)
      -> compT Xs g Z
comp'  []      f y = f y
comp' (X ∷ Xs) f g = λ (x : X) -> comp' Xs f (g x)
现在很容易使类型隐式化:

comp : ∀ {α β γ} {Y : Set β} {Z : Y -> Set γ}
     -> (n : ℕ)
     -> ∀⇒ (λ (Xs : Vec (Set α) n)
           -> (f : (y : Y) -> Z y)
           -> (g : Vec-ary Xs Y)
           -> compT Xs g Z)
comp n = λ⇒ {n} comp'
和测试:

zeros : (n : ℕ) -> Vec ℕ n
zeros  0      = []
zeros (suc n) = 0 ∷ zeros n

comp-test-func-1 : ℕ -> Vec ℕ 0 -> Vec (Vec ℕ 1) 2 -> ℕ
comp-test-func-1 _ _ _ = 3

test-comp-1 : ℕ -> Vec ℕ 0 -> Vec (Vec ℕ 1) 2 -> Vec ℕ 3
test-comp-1 = comp 3 zeros comp-test-func-1
但是有一个缺点:所有类型必须位于同一个宇宙中,因此此测试无法通过:

comp-test-func-2 : Set -> Vec ℕ 0 -> Vec (Vec ℕ 1) 2 -> ℕ
comp-test-func-2 _ _ _ = 3

test-comp-2 : Set -> Vec ℕ 0 -> Vec (Vec ℕ 1) 2 -> Vec ℕ 3
test-comp-2 = comp 3 zeros comp-test-func-2
因为
Set
Vecℕ 0
位于不同的宇宙中

但是,可以生成一个完全多态的复合函数,但只能使用半显式参数。所以
comp3
变成
comp(\u∷ _ ∷ _ ∷ [])
,这很难看