Haskell的多态性

Haskell的多态性,haskell,types,polymorphism,ghci,parametric-polymorphism,Haskell,Types,Polymorphism,Ghci,Parametric Polymorphism,我是哈斯克尔的新手,读《第一原理》中的哈斯克尔 顺便说一句,这个问题与本书无关,我以下面的问题为例 在第10章,第10.5节,Q 5,f部分 问题: 以下是与您已经完成的非常相似的简单折叠 已看到,但每个都至少有一个错误。请把它们修好,测试一下 你的答复 f) foldr const'a'[1..5] 它给出了以下错误 没有由文字“1”产生的(Num Char)实例 简单地说,这里不能将1用作字符 但我在本章中读到,折叠就像文本重写器,用函数替换cons(:),用累加器替换空列表 因此结果是(

我是哈斯克尔的新手,读《第一原理》中的哈斯克尔

顺便说一句,这个问题与本书无关,我以下面的问题为例

在第10章,第10.5节,Q 5,f部分

问题:

以下是与您已经完成的非常相似的简单折叠 已看到,但每个都至少有一个错误。请把它们修好,测试一下 你的答复

f) foldr const'a'[1..5]

它给出了以下错误 没有由文字“1”产生的(Num Char)实例

简单地说,这里不能将1用作字符

但我在本章中读到,折叠就像文本重写器,用函数替换cons(:),用累加器替换空列表 因此结果是(我缩短了列表)

它的工作没有编译器错误

那么会出什么问题呢? 为什么第一个不起作用,而它的重写却起作用

但是我看到在重写的形式中,
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修复了它的类型,而我们不能以其他形式使用它

也许应该是这样的

cont :: Int -> Int -> Int
cont = const
折叠类似于文本重写器,用函数替换cons(:),修复函数类型,用累加器替换空列表

请分享你的想法

不仅Haskell而且其他类型化语言都有相同的行为


或者我完全错了?

我认为,它更简单,函数const不符合foldr所期望的签名:

前奏曲>:t foldr
可折叠的t=>(a->b->b)->b->TA->b
前奏曲>:t常量
常数:a->b->a

因此haskell试图概括
a
b
(找出它们都有足够的类型类集),最后,得出
1
是一个数字,但“c”并没有实现这个类型类。

您的分析是正确的,尽管在细节上有所欠缺。如果你愿意,你可以在更高的层次上看到这个问题

foldr :: (a -> r -> r) -> r -> [a] -> r
const :: b -> c -> b
(如果我选择了不同于您习惯的类型变量,请原谅——alpha equivalence说变量名无关紧要,保持名称不同可以更容易地看到发生了什么。)

如果您使用这些类型并统一事物以使
foldr const
工作,您将得到如下结果:

(a -> r -> r) ~ (b -> c -> b)
a ~ b (first arg)
r ~ c (second arg)
r ~ b (result)
r ~ a (by transitivity)

foldr const :: a -> [a] -> a (substitution)
其中~表示“这些类型相同”

因此,您可以从
foldr
const
中看到列表元素的类型必须与其他参数的类型统一

但是,如果在类型检查之前,
foldr
有完整的内联表达式,那么结果表达式将成功检查,这是正确的。但这是一个有点不够的情况。如果您执行了
foldr const'a'[]
,该怎么办?因为这将减少到
'a'
,所以它也会进行类型检查——但是它会使用不同的类型,而不是传递给它的列表是非空的

这正是Haskell型系统想要防止的问题。表达式的类型应完全从其子表达式的类型派生。不应考虑值(*),因此无论子表达式是空列表还是非空,表达式都需要具有相同的结果类型

(*)好的,GHC支持的比Haskell 2010多一点。像GADT这样的扩展可以根据值启用某种类型,但只能以非常特定的方式来保持更大的一致性属性。

在表达式中

(1 `const` (2 `const` 'a')) 
   ---A---    ---B---
(\c -> 1 `c` (2 `c` 'a')) const
我们使用多态函数
const
两次。每次使用都会将多态函数类型实例化为进行类型检查所需的任何类型

const
的一般类型为:

const :: a -> b -> a
让我们假设数值文本具有type
Int
,以保持解释的简单性。 当我们写
2`const`'a'
(上面的点
--B--
)时,Haskell知道上面的类型变量
a
必须是
Int
,而
B
必须是
Char
,所以这里我们使用

const :: Int -> Char -> Int
const :: Int -> Int -> Int
因此,
2`const`'a'
的结果类型为
Int

知道了这一点,我们现在可以认识到,仅当我们选择类型变量
A
b
作为
Int
时,在上面的
--A--
点使用的
常量才能进行类型检查。因此,我们正在使用

const :: Int -> Char -> Int
const :: Int -> Int -> Int
这是可能的,因为类型系统允许在函数使用的每个点上进行不同的多态类型实例化


值得注意的是,这个表达式

(1 `const` (2 `const` 'a')) 
   ---A---    ---B---
(\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
所以,当我
1 `const` 2
1
example :: (forall a. a -> a) -> (Int, Bool)
example f = (f 1, f True)