Haskell 您能否创建在依赖类型语言中返回依赖类型函数的函数?

Haskell 您能否创建在依赖类型语言中返回依赖类型函数的函数?,haskell,agda,dependent-type,idris,Haskell,Agda,Dependent Type,Idris,根据我对依赖类型的了解,我认为这应该是可能的,但我以前从未在依赖类型语言中看到过这样的例子,所以我不确定从哪里开始 我想要的是形式的函数: f : [Int] -> (Int -> Bool) f : [Int] -> (Int -> Int -> Bool) f : [Int] -> (Int -> Int -> Int -> Bool) 等等 此函数获取一个nint的列表,并返回一个以int为参数的arity n谓词函数。这种事情在依赖

根据我对依赖类型的了解,我认为这应该是可能的,但我以前从未在依赖类型语言中看到过这样的例子,所以我不确定从哪里开始

我想要的是形式的函数:

f : [Int] -> (Int -> Bool)
f : [Int] -> (Int -> Int -> Bool)
f : [Int] -> (Int -> Int -> Int -> Bool)
等等


此函数获取一个n
int的列表,并返回一个以int为参数的arity n谓词函数。这种事情在依赖类型的语言中可能发生吗?如何实现这样的功能?

在创建不同类型的函数时,通常需要从值到类型的函数。在这种情况下,我们需要一个来自
列表的函数ℕ(或者干脆
-列表的长度)设置为

Predicate : ℕ → Set
Predicate zero    = Bool
Predicate (suc n) = ℕ → Predicate n
这允许我们为每个数字创建不同的类型:

Predicate 0 = Bool
Predicate 1 = ℕ → Bool
Predicate 2 = ℕ → ℕ → Bool
-- and so on

现在,我们如何使用
谓词
来表示
f
的类型?因为我们使用的是依赖类型的语言,所以我们可以在类型中自由使用值级函数。所以
length
似乎是一种自然的搭配:

f : (l : List ℕ) → Predicate (length l)
现在,您没有指定任何特定的
f
,但是为了示例,我将实现一个。假设我们要检查对应位置的所有数字(即,具有列表第一个元素的函数的第一个参数等)是否相等

我选择了一个相当简单的函数,因此实现将非常简单。但请注意,这类函数使用了各种技巧,与使用类型类实现可变函数(在Haskell中)所用的技巧没有什么不同

当给出一个空列表时,我们只需返回
true

f []       = true
对于非空列表,我们创建一个函数,使用一个名为
n
的参数,然后将其与列表的开头(
m
)进行比较。如果这些数字不相等,我们将缩短
f
的其余部分,并返回一个忽略所有其他数字的函数,只返回
false
;如果这些数字相等,我们将继续下面的列表:

f (m ∷ ms) = λ n → case m ≟ n of λ
  { (yes _) → f ms
  ; (no  _) → always-false
  }
辅助函数
始终为false

always-false : ∀ {n} → Predicate n
always-false {zero}  = false
always-false {suc _} = λ _ → always-false
下面是您如何使用它:

test : Bool
test = f (1 ∷ 2 ∷ 3 ∷ []) 1 2 4  -- false


最后一句话:当你没有任何关于你应用它的参数的信息时,这些函数不是很有用。例如,如果对长度未知的列表(作为另一个函数的参数提供)使用
f
,则甚至不能将“谓词”应用于数字。列表很可能是空的,在这种情况下谓词是
Bool
,不能应用于任何东西。

我记得斯蒂芬妮·魏里奇(Stephanie Weirich)在Agda中关于算术泛型编程的一篇文章,你可能会发现这篇文章是相关的。这比你所问的要远得多,但导言部分可能会提供一个很好的解释。

@Vitus已经提出了一个使用依赖类型的Agda解决方案。在这里,我对Haskell进行了评论,因为您添加了它的标签

在Haskell中,我们没有Agda中的依赖类型,因此无法在类型中写入
length l
。但是,我们可以使用自定义列表GADT,它使用Peano naturals在类型级别公开列表的长度

data Z
data S n

data List n a where
   Nil :: List Z a
   Cons :: a -> List n a -> List (S n) a
然后,我们可以使用类型族来计算具有
n
参数的类型
(a->a->…->Bool)
,其中
n
是给定的类型级别

type family Fun n a
type instance Fun Z     a = Bool
type instance Fun (S n) a = a -> Fun n a
下面是你如何使用它。下面将该列表与提供的“参数列表”进行比较

equalityTest :: Eq a => List n a -> Fun n a
equalityTest = go True
  where go :: Eq a => Bool -> List n a -> Fun n a
        go b Nil = b
        go b (Cons x xs) = \y -> go (x==y && b) xs

-- *ListGADT> equalityTest (Cons 1 (Cons 2 Nil)) 1 2
-- True
-- *ListGADT> equalityTest (Cons 1 (Cons 2 Nil)) 1 3
-- False

另外,我不久前写过variadic
zipWith
,您可能会发现它很有用: