我正在努力研究R. Bird的函数式编程书中的练习,该书要求使用类型(num - > num) - >的函数的示例。 NUM
我能想到的最好的是多态类型
func1 f = f 3
:t func1
func1 :: Num t1 => (t1 -> t2) -> t2
我遇到的问题是我无法指定f的返回类型,因此类型仍为(num - > t2) - > T2。
我强制返回f类型的尝试如下:
square x = x * x
:t func1 square
func1 square :: Num t2 => t2 -> t2
因为当然如果我试图找到func1∘square的类型,它将只是num - > NUM
答案 0 :(得分:3)
如果足以提供可以分配该类型的功能,那么你的已经足够了。也就是说,以下类型检查就好了:
func1 :: Num a => (a -> a) -> a
func1 f = f 3
另一方面,如果你想要一个被推断为具有该类型的函数,那么你需要做一些技巧。我们在这里要做的是指定f 3
的结果和我们输入的3
具有相同的类型。强制两个术语具有相同类型的标准方法是使用asTypeOf
,这是以这种方式实现的:
asTypeOf :: a -> a -> a
asTypeOf x _ = x
所以,试试吧:
> :t \f -> f 3 `asTypeOf` 3
(Num a, Num t) => (t -> a) -> a
对我们来说不幸的是,这不起作用,因为3
中的f 3
和独立的3
被推断为使用可能不同的 Num
的实例。不过,它比\f -> f 3
更接近 - 请注意我们之前没有输出的新Num a
约束。明显的下一个想法是let
- 将变量绑定到3
并将该变量重用为f
和asTypeOf
的参数;当然GHC会得到f
的论点和结果具有相同类型的图片,对吗?
> :t \f -> let x = 3 in f x `asTypeOf` x
(Num a, Num t) => (t -> a) -> a
讨厌鬼。事实证明let
做了所谓的"让我们进行推广&#34 ;; x
将与3
一样多态,并且可以专门针对不同使用站点的不同类型。通常这是一个很好的功能,但因为我们做了一个不自然的运动,我们需要做一些不自然的事情......
好的,下一个想法:一些lambda calculi不包含let
,当你需要一个时,而不是写let a = b in c
,你写(\a -> c) b
。这对我们来说特别有趣,因为Haskell使用特殊限制的多态,这意味着在c
内,a
的类型是单态。所以:
> :t \f -> (\x -> f x `asTypeOf` x) 3
Num a => (a -> a) -> a
现在你抱怨asTypeOf
作弊,因为它使用了与其推断类型不匹配的类型声明,而练习的重点是通过推理单独获得正确的类型。 (如果我们可以使用与推断类型不匹配的类型声明,我们可以在开始时从func1 :: Num a => (a -> a) -> a; func1 f = f 3
停止!)好的,没问题:还有另一个标准强制两个表达式的类型统一的方法,即将它们放在一个列表中。所以:
> :t \f -> (\x -> head [f x, x]) 3
Num a => (a -> a) -> a
Phew,现在我们终于可以在原则上从头开始构建所需的所有工具,无需任何类型声明即可获得正确类型的术语。
答案 1 :(得分:0)
func1 f = let x = f x in x
这是一个部分功能,它在技术上具有您想要的类型,您应该知道它们是什么以及它们在haskell中的工作方式。