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