作为我的Haskell学习过程的一部分,我喜欢明确地输出函数的类型声明.我希望能够为where子句中定义的函数执行此操作,但我不知道如何指定,where子句中的类型变量应该表示与外部类型声明中的某个类型变量相同的类型.例如,以下代码:
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: a -> a
bar a = f a
Run Code Online (Sandbox Code Playgroud)
产生此错误:
src\Test.hs:7:14:
Couldn't match expected type `a' against inferred type `a1'
`a' is a rigid type variable bound by
the type signature for `foo' at src\Test.hs:3:8
`a1' is a rigid type variable bound by
the type signature for `bar' at src\Test.hs:6:11
In the first argument of `f', namely `a'
In the expression: f a
In the definition of `bar': bar a = f a
Run Code Online (Sandbox Code Playgroud)
我如何表达bar的第一个参数应该与foo的第二个参数类型相同,以便我可以将f应用于它?
谢谢.
Chr*_*icz 13
我认为你可以通过GHC支持的ScopedTypeVariables来做到这一点.这肯定编译:
{-# LANGUAGE ScopedTypeVariables #-}
foo :: forall a. (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: a -> a
bar a = f a
Run Code Online (Sandbox Code Playgroud)
注意"forall a."
还有另一种解决方法。不是f
在内部函数中引用,而是bar
扩展bar
为接受f
作为第一个参数并在父级中使用部分应用程序。
foo :: (a -> a) -> a -> a
foo f arg = (bar f) arg
where
bar :: (a -> a) -> a -> a
bar f a = f a
Run Code Online (Sandbox Code Playgroud)
它不需要 ScopedTypeVariables 或显式类型检查代码作为其他答案。
为了清楚起见,让我们改变类型参数bar
来b
,也重新命名它的参数。
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: b -> b
bar x = f x
Run Code Online (Sandbox Code Playgroud)
Haskell 抱怨是因为bar
被注释为b -> b
(对于任何任意类型 b
),但f x
试图将类型参数应用于类型b
函数a -> a
(对于特定的 bound a
)。换句话说,内部函数并不像它的类型注释所宣传的那样通用。
传递f
tobar
意味着对于表达式(bar f)
,类型变量b
绑定到与 相同的类型a
。
最后,在不更改任何其他内容的情况下,如果您愿意省略内部函数的类型签名bar
,Haskell 将完全按照您想要的方式推断其类型。也就是说,由于bar
适用于f
从父函数foo
的类型,bar
将重用类型参数a
的类型foo
。
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
-- Type: bar :: a -> a
bar a = f a
Run Code Online (Sandbox Code Playgroud)