依赖类型可以通过n-arg函数进行抽象吗?

时间:2014-06-11 02:15:05

标签: types dynamic-typing static-typing dependent-type

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

例如Clojure中的memoize函数。

(def memoized-fn 
  (memoize any-function))

在此示例中,memoize并不关心any-function引用的函数或接受的参数数量*。

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


在以前的生活中,我想用静态类型语言创建类似的东西,在我的情况下,我正在使用Scala,而Scala有很多FunctionN种类型(1个arg,最多23个,我相信),但没有任何函数之间的关系似乎没有办法利用它们的函数和创建单个泛型函数。

最后我有这样的事情*

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])

(实际上,我在Fn 4或5周围停了下来,因为虽然我很高兴Function23能够存在,但我从不想真正使用它。)

*这也可能是psuedo-Scala代码,自从我编写任何代码以来已经有一段时间了。


回到今天:我理解,对于依赖类型,我可以创建一个函数,该函数接受带有值的参数参数化。这个好的示例似乎是一个函数,它采用任何类型和大小的列表n并返回相同大小的列表n。

我可以用同源列表(大小为n的A类列表)来理解这一点,但我不知道它是否可能存在异构列表。

由此我假设我可以创建一个带有n个相同类型参数的函数。有点像:

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

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

另请注意:请将memoize示例和上面使用的语言视为我的想法的示例,我不是要问如何使用静态类型语言进行记忆,而是将备忘录代码作为更高级别的问题一个例子。

*请原谅我在这个问题上缺乏更好的词汇,我还在学习很多这方面的知识。我们也欢迎有关编辑/改进的建议。

2 个答案:

答案 0 :(得分:0)

依赖类型(pis)基本上是“强化”形式的函数空间(箭头)。

如果你看到:

Pi x : T. T(x)

然后它是一种基本上说“给我一个T类型的x的类型,我会给你一些T(x)类型的东西”

在堕落的情况下,T在其正文中没有x的任何实例,这相当于其他语言中的T1 -> T2(用T1代替xPi x : nat. listoflength(x) 以上)。对你的问题的简单回答是,不,你通常不能增加一个类型中的“箭头数量”,但你可以做一些基本上是这样的事情,如果你有这个。

listoflength

其中x是生成长度为n的列表(某种类型,可能是另一个参数)的东西。例如,您可以使用sigma类型实现它(例如,具有长度的列表,因为它的prop参数ala this technique)。问题在于论证必须是同质的。另一种方法是创建一个函数,给定一个参数,给你一个n元组的元组,每个元组都有一些类型。

基本问题是,如果你提供一个特定的n,那么你需要能够说出“我接受n个参数,这里是他们的类型”,以制作所有类型算术运算。

因此,虽然您无法创建任意数量的箭头,但您可以创建一些生成函数的函数,该函数将元组作为输入(我想如果您知道{{1}},则可以在这些位置处取消它它被使用了。)

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

答案 1 :(得分:0)

如果您知道诀窍,那么编写异构arity-generic函数很容易,这在Arity-Generic Datatype-Generic Programming文章中有所描述。首先,你创建一个函数,它接收一个类型的向量,其次,你" 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-generic组合函数,它接收类型的显式向量。

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

但是有一个缺点:所有类型必须位于同一个Universe中,因此不会传递此测试:

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

因为SetVec ℕ 0位于不同的宇宙中。

但是,可以创建一个完全多态的组合函数,但只能使用半显式参数。因此,comp 3变为comp (_ ∷ _ ∷ _ ∷ []),这很难看。