Haskell 兰克恩泰普斯:是什么导致了这个错误?
我只是在探索RANK2类型和RankNTypes,试图熟悉它们。但是我不明白为什么下面的方法不起作用Haskell 兰克恩泰普斯:是什么导致了这个错误?,haskell,types,ghc,Haskell,Types,Ghc,我只是在探索RANK2类型和RankNTypes,试图熟悉它们。但是我不明白为什么下面的方法不起作用 g :: (forall a. forall b. a -> b) -> x -> y -> (u,v) g p x y = (p x, p y) 编译器接受此定义,但尝试使用时失败: ghci> g id 1 2 <interactive>:35:3: Couldn't match type `a' with `b' `a' i
g :: (forall a. forall b. a -> b) -> x -> y -> (u,v)
g p x y = (p x, p y)
编译器接受此定义,但尝试使用时失败:
ghci> g id 1 2
<interactive>:35:3:
Couldn't match type `a' with `b'
`a' is a rigid type variable bound by
a type expected by the context: a -> b at <interactive>:35:1
`b' is a rigid type variable bound by
a type expected by the context: a -> b at <interactive>:35:1
Expected type: a -> b
Actual type: a -> a
In the first argument of `g', namely `id'
In the expression: g id 1 2
ghci>gid12
:35:3:
无法将类型“a”与“b”匹配
`a'是一个刚性类型变量,由
上下文所需的类型:a->b at:35:1
`b'是一个刚性类型变量,由
上下文所需的类型:a->b at:35:1
预期类型:a->b
实际类型:a->a
在'g'的第一个参数中,即'id'
在表达式中:gid12
我很难理解为什么
a->a
对于所有类型a
和b
对于所有a的a
和b
类型的函数来说都是不可接受的类型。福尔b。a->b必须能够获取a
类型的值并生成b
类型的值。因此,例如,必须能够输入一个Int
,然后输出一个字符串
如果输入Int
,则无法从id
中获取字符串
——您只能返回输入的相同类型。因此,id
不是a的类型。福尔b。a->b
。事实上,如果没有类型类约束,就不可能有该类型的全部函数
事实证明,你可以使用ConstraintKinds做一些接近你想要做的事情,但它既不好写,也不好用: 其思想是使用约束来参数化
g
,这些约束指定x
、y
、u
和v
需要满足哪些条件,以及x
和u
之间以及y
和v
之间需要什么关系。因为我们在所有情况下都不需要所有这些约束,所以我们还引入了两个伪类型类(一个用于单个参数的约束,一个用于“关系约束”),这样我们就可以在不需要约束的情况下使用它们作为约束(如果我们不自己指定约束,GHC无法推断约束)
一些示例约束使这一点更加明确:
- 如果我们将
作为函数传入,id
必须等于x
,u
必须等于y
。对v
、x
、y
或u
分别没有任何约束v
- 如果我们传入
,show
和x
必须是y
和show
的实例,u
必须等于v
。对String
和x
或u
和y
之间的关系没有任何约束v
- 如果我们传入
,read.show
和x
需要是y
的实例,show
和u
需要是v
的实例。同样,它们之间的关系没有约束read
- 如果我们有一个类型类
并传入Convert a b,其中Convert::a->b
,那么我们需要Convert
和Convert x u
,并且对单个参数没有约束Convert y v
{-# LANGUAGE Rank2Types, ConstraintKinds, FlexibleInstances, MultiParamTypeClasses #-}
class Dummy a
instance Dummy a
class Dummy2 a b
instance Dummy2 a b
g :: forall c. forall d. forall e. forall x. forall y. forall u. forall v.
(c x, c y, d u, d v, e x u, e y v) =>
(forall a. forall b. (c a, d b, e a b) => a -> b) -> x -> y -> (u,v)
g p x y = (p x, p y)
下面是如何使用它:
ghci> g id 1 2
<interactive>:35:3:
Couldn't match type `a' with `b'
`a' is a rigid type variable bound by
a type expected by the context: a -> b at <interactive>:35:1
`b' is a rigid type variable bound by
a type expected by the context: a -> b at <interactive>:35:1
Expected type: a -> b
Actual type: a -> a
In the first argument of `g', namely `id'
In the expression: g id 1 2
使用show.read
在不同类型的数字之间转换:
> (g :: (Show x, Show y, Read u, Read v, Dummy2 x u, Dummy2 y v) => (forall a. forall b. (Show a, Read b, Dummy2 a b) => a -> b) -> x -> y -> (u,v)) (read . show) 1 2 :: (Double, Int)
(1.0,2)
使用id
:
> (g :: (Dummy x, Dummy y, x~u, y~v) => (forall a. forall b. (Dummy a, Dummy b, a~b) => a -> b) -> x -> y -> (u,v)) id 1 2.0
(1,2.0)
使用显示:
> (g :: (Show x, Show y, String~u, String~v, Dummy2 x u, Dummy2 x y) => (forall a. forall b. (Show a, String~b, Dummy2 a b) => a -> b) -> x -> y -> (u,v)) show 1 2.0
("1","2.0")
如您所见,这非常冗长且不可读,因为每次使用时都需要为g
指定一个签名。没有这一点,我认为GHC不可能正确推断约束(或者至少我不知道如何).当你看一个函数时,用于所有a.用于所有b.a->b
意味着它接受任何类型的值,并且可以返回任何类型的值。假设f
是这样一个函数,那么你可以将sayf1
馈送给任何函数或f“hello”“
到任何函数,因为f的返回类型仍然是多态的,而另一方面,一旦您给id
一个值,返回类型是固定的(它将与输入值的类型相同),因此会出现错误 对于所有a
和b
,您需要类型为a->b
的函数。因为id
是所有a的类型。a->a
(即,如果a~b
,它只满足第一个签名)它不是g
的有效输入。那么,如何表示p
的类型可以采用任何类型并返回任何类型?也就是说,它可以是id
或show
或(<10)
等。给定函数的实现,它的类型应该是什么?@PeterHall你不能。另外,无论哪种方式,您都无法传入show
,因为x
和y
不是show
的实例。啊,我现在已经理解了其中的一部分。如果我需要参数是Int
和Bool
,但结果是(Int,String)
,那么没有这样的函数可以满足这一点。编译器无法推断出这一事实,但它将拒绝我尝试的所有内容。@PeterHall没有完全可以满足这一点的函数。像fx=fx
(或者干脆未定义的)这样的东西可以很容易地满足这一点。如果添加一些类型类约束,甚至可以得到满足它的全部函数,例如read。如果x
和y
是show
的实例,则显示;如果u
和v
是Read
的实例,则显示。使用ConstraintKinds,您甚至可以使其具有某种通用性/实用性。不过,你仍然无法让它实现你的想法。@PeterHall发现,有了KindConstraints,b,你可以做你想做的事情