11 haskell higher-order-functions parametric-polymorphism
来自Javascript我明白Haskell的列表类型强制实施同类列表.现在让我感到惊讶的是,以下不同的功能类型满足此要求:
f :: (a -> a) -> a -> a
f g x = g x
g :: (a -> b) -> a -> b
g h x = h x
let xs = [f, g] -- type checks
Run Code Online (Sandbox Code Playgroud)
即使g比f以下更广泛适用:
f(\x -> [x]) "foo" -- type error
g(\x -> [x]) "foo" -- type checks
Run Code Online (Sandbox Code Playgroud)
不(a -> a)应该比不加善待(a -> b).在我看来,好像后者是前者的子类型.但是Haskell中没有子类型关系,对吧?那么为什么这样呢?
lef*_*out 12
Haskell是静态类型的,但这并不意味着它是Fortran.每种类型都必须在编译时得到修复,但不一定在单个定义中.该两种类型的f和g是多态.解释这一点的一种方法是,f不仅仅是单个函数,而是一整套重载函数.喜欢(在C++中)
int f (function<int(int)> g, int x) { return g(x); }
char f (function<char(char)> g, char x) { return g(x); }
double f (function<double(double)> g, double x) { return g(x); }
...
Run Code Online (Sandbox Code Playgroud)
当然,实际生成所有这些函数是不切实际的,所以在C++中你会把它写成模板
template <typename T>
T f (function<T(T)> g, T x) { return g(x); }
Run Code Online (Sandbox Code Playgroud)
...意思是,每当编译器发现f你的项目代码时,它会弄清楚T具体情况是什么,然后创建一个具体的模板实例化(一个固定到那个具体类型的单态函数,就像我上面写的那些例子)并且仅在运行时使用该具体实例化.
这两个模板函数的具体实例可能具有相同的类型,即使模板看起来有点不同.
现在,Haskell的参数多态性与C++模板有一点不同,但至少在你的例子中它们相同:g是一整套函数,包括实例化g :: (Int -> Char) -> Int -> Char(与类型不兼容f),但是g :: (Int -> Int) -> Int -> Int,是的.当你把f和g在单个列表中,编译器会自动实现,只有亚科g其类型是兼容f与此有关.
是的,这确实是一种子类型.当我们说"Haskell没有子类型"时,我们的意思是任何具体(Rank-0)类型与所有其他Rank-0类型不相交,但是多态类型可能重叠.