Haskell中的多态性
我是 Haskell 的新手,从第一原则开始阅读 Haskell。
顺便说一句,这个问题一般与本书无关,我以以下问题为例
在第 10 章,第 10.5 节,Q 5,f 部分
题:
以下是与您已经看到的非常相似的简单折叠,但每个折叠都至少有一个错误。请修复它们并在您的 REPL 中测试
f) 文件夹 const 'a' [1..5]
并且它给出了以下错误 No instance for (Num Char)来自文字“1”
只是意味着 1 不能在这里用作 Char
但我在本章中读到折叠就像文本重写器,用函数替换 cons(:) 并用累加器替换空列表,所以结果是(我缩短了列表)
foldr const 'a' [1..2]
to
(1 `const` (2 `const` 'a'))
并且它的工作没有编译器错误
那么有什么问题呢?为什么第一个不工作,它的重写工作?
但我看到重写的形式const有两种形式
Int -> Int -> Int
and
Int -> Char -> Int
也许这是它的BCS所以我固定的类型,const像这样
cont :: Int -> Int -> Int
cont = const
现在当我说使用它时
(1 `cont` (2 `cont` 'a'))
Couldn't match expected type `Int' with actual type `Char'
好像当我们使用多态函数时 Haskell 修复了它的类型,我们不能以其他形式使用它
也许它应该是列表折叠描述应该是这样的
folds like Text Rewriter,用函数替换cons(:),修复函数类型,用Accumulator替换空列表
请分享您对此的看法。
不仅 Haskell,其他类型语言也有相同的行为?
或者我完全错了?
回答
在表达式中
(1 `const` (2 `const` 'a'))
---A--- ---B---
我们使用了const两次多态函数。每次使用都会导致多态函数类型被实例化为进行所有类型检查所需的任何类型。
一般类型const为:
const :: a -> b -> a
让我们假设数字文字具有类型Int以保持解释简单。当我们写2 `const` 'a'(--B--上面的点)时,Haskell 理解a上面的类型变量必须是Int,而b必须是Char,所以这里我们使用
const :: Int -> Char -> Int
因此, 的结果2 `const` 'a'是 类型Int。
知道了这一点,我们现在可以意识到上面const使用的 at 点--A--只有在我们选择类型变量a和bas 时才能进行类型检查Int。所以,我们正在使用
const :: Int -> Int -> Int
这是可能的,因为类型系统允许在使用函数的每个点都有不同的多态类型实例化。
注意到表达式可能会很有趣
(c -> 1 `c` (2 `c` 'a')) const
是一种简化(技术上:beta-reduces)原始表达式的表达式,但不可输入。这里,const只使用了一次,所以它的类型只能实例化一次,但是没有一种类型实例化可以同时用于这两个c点。这是类型推断引擎的一个已知限制,在执行 Hindley-Milner 推断的所有函数式语言之间共享。
您的使用foldr const ....共享相同的问题。
起初看到我们可能有两个表达式可能会令人困惑,一个简化为另一个,但只有一个是可键入的。然而,这是生活中的事实。理论上,Haskell 满足以下“主题归约”性质:
- 如果
e1输入e1了表达式e2,并简化为表达式,则e2输入表达式
但是,Haskell(与大多数语言一样)无法满足以下“主题扩展”属性:
- 如果
e2输入e1了表达式e2,并简化为表达式,则e1输入表达式
因此,尽管这是不可能的是一个类型表达式简化为一个不适类型之一,它是可能的是一个不适输入表达式简化为一个类型之一。一个例子就是你找到的那个。一个更简单的是(x -> True) ("ill-typed" "term")类型错误的(它使用字符串作为函数)但简化为True类型良好的。