为什么Haskell在尝试解析此类型签名时会抱怨?
f :: (a,s) -> (forall r.(r -> (a,r)),s)
答案 0 :(得分:6)
Haskell不支持impredicative类型,特别是不允许forall
出现在任何类型构造函数下(->
除外)。
例如,Maybe (forall a. a), [forall a. a->a], ((forall a. a), Bool)
被禁止。
如果您想要的话,请使用newtype
包装。
newtype T = T (forall a. a->a)
foo :: [T] -- OK
foo = [T id]
答案 1 :(得分:4)
出于好奇,你想用这种类型做什么?它看起来像quote
这样的连接组合类型,[X] quote == [[X]]
(有时称为unit
)。换句话说,它在堆栈顶部采用一个值并将其包装在一个函数中,该函数在应用时将该值推送到堆栈。
这是我过去用于此类功能的一种表示。 Tupled
类型系列将类型列表转换为嵌套元组以表示堆栈。
-- Tupled '[t1, t2, ..., tn] s == (t1, (t2, (... (tn, s))))
type family Tupled ts t where
Tupled '[] t' = t'
Tupled (t ': ts) t' = (t, Tupled ts t')
使用newtype
包装器,我们可以创建一个函数(某个输入和输出arity),它在堆栈的“rest”中是多态的。
newtype as :-> bs = Fun (forall s. Tupled as s -> Tupled bs s)
这是隐藏 impredicative polymorphism 的标准方法,即在函数箭头forall
以外的类型构造函数下使用(->)
- 量化类型,就像你做的那样当你试着写(forall r. (r -> (a, r)), s)
时。 Haskell不直接支持这个,但是如果你使用newtype
包装器,那么编译器就知道何时引入并消除forall
。
通过展开此newtype
并将其应用于堆栈类型,我们可以将包装函数应用于堆栈。
apply :: forall z as bs. (as :-> bs) -> Tupled as z -> Tupled bs z
apply (Fun f) as = f @z as
quote
组合器在函数中包装堆栈的顶部元素:
quote :: forall a s. (a, s) -> ([] :-> '[a], s)
quote (a, s) = (Fun $ \s' -> (a, s'), s)
unquote
将堆栈上的函数应用于堆栈的其余部分。
unquote
:: forall z as bs s
. (Tupled as z ~ s)
=> (as :-> bs, s)
-> Tupled bs z
unquote (f, s) = apply @z f s
(注意等式约束Tupled as z ~ s
,这意味着“输入堆栈类型s
必须以一系列类型as
开头,剩下的任何类型称为z
” 。)
add
是加法运算符(+)
被提升到堆栈;它只是添加了堆栈的前两个元素。
add :: forall a. (Num a) => '[a, a] :-> '[a]
add = Fun $ \ (x, (y, s)) -> (x + y, s)
现在,引用和取消引用元素会使其保持不变:
unquote (quote ("hello", ()) == ("hello", ())
可以直接应用添加功能......
apply add (1, (2, ())) == (3, ())
...或者放在堆叠上然后应用。
unquote (add, (1, (2, ()))) == (3, ())
这需要以下扩展名:
DataKinds
允许类型级别的类型列表
RankNTypes
和ScopedTypeVariables
允许显式forall
并将类型变量纳入范围,以便我们可以使用TypeApplications
引用它们,因为我们需要{{ 1}}延迟指定“堆栈”类型,直到调用网站,如AllowAmbiguousTypes
apply @z f as
启用TypeFamilies
类型系列
Tupled
为包装的函数类型提供漂亮的符号名TypeOperators