Haskell 为什么是a->;b->;c函数参数可以接受a->;a型?

Haskell 为什么是a->;b->;c函数参数可以接受a->;a型?,haskell,Haskell,这将成功编译: test = f id f :: (a -> b -> c) -> String f g = "test" id的类型为a->a,但f的第一个参数的类型为(a->b->c) 我认为它不应该编译。我无法理解它。因为您可以绑定未实例化的变量f::(a->b->c)->String表示,对于任意3种类型的a、b和c,f从a到b到c取一个函数并返回String 重要的是要记住,f:(a->b->c)->String与f:(a->(b->c))->

这将成功编译:

test = f id
f :: (a -> b -> c) -> String
f g =  "test"
id
的类型为
a->a
,但
f
的第一个参数的类型为
(a->b->c)


我认为它不应该编译。我无法理解它。

因为您可以绑定未实例化的变量
f::(a->b->c)->String
表示,对于任意3种类型的a、b和c,f从a到b到c取一个函数并返回String

重要的是要记住,
f:(a->b->c)->String
f:(a->(b->c))->String
是等价的,因为使用了curry

id
接受任何类型,并返回该类型


所以如果我们把它换成,
id
返回
b->c
,然后取
a
,那么如果
a
可以是
b->c
,它可以是任何类型的,那么这很好。

短版本:它工作的原因是
id
可以接受并返回函数,因此它的行为就好像它是一个双参数函数一样。现在来看长版本

id
是一个函数,它接受一个值并返回不变的值。因为它非常简单,所以它可以对各种不同的值--
Int
s和
Bool
s和
String
s以及列表和树

id :: Int -> Int
id :: Bool -> Bool
id :: String -> String
id :: [(b, c)] -> [(b, c)]
id :: Tree b -> Tree b
id :: (b -> c) -> (b -> c)
…和功能,为什么不呢

f
是一个接受两个参数函数并忽略它们的函数。因为它对你给它的函数的处理非常简单,它可以对各种不同的两个参数函数——加法、串联、配对、忽略、打印

f :: (Int -> Int -> Int) -> String
f :: ([b] -> [b] -> [b]) -> String
f :: (b -> c -> (b, c)) -> String
f :: (b -> c -> b) -> String
f :: (Handle -> String -> IO ()) -> String
f :: ((b -> c) -> b -> c) -> String
…以及接受函数和参数并将其应用于另一个的操作,为什么不呢

但是两个参数函数实际上只是一个参数函数,它们本身返回一个函数。因此,这两种类型实际上只是同一类型的不同拼写:

事实证明,我对这两种类型给出的两种行为描述也是一样的。回想起来,它们是:

  • 将函数作为参数并返回不变的函数
  • 一种函数,它将一个函数作为参数,并将该函数的参数应用于另一个函数
  • 我会让你苦思冥想一下为什么这些结果都是一样的

    在任何情况下,在这一点上,我们应该更加清楚为什么这是可行的。对于机械版本的答案,我们可以采用以下两种类型:

    id :: a -> a
    f :: (a -> b -> c) -> String
    
    并通过为两者选择
    a~b->c
    使它们组合在一起。(这只是巧合,
    a
    的两个实例化属于同一类型——进行统一时通常不是这样!)在两个类型签名中实例化
    a
    b->c
    后,我们得到:

    id :: (b -> c) -> b -> c
    f :: ((b -> c) -> b -> c) -> String
    

    提示:表达式
    id not
    id
    的类型是什么?我不明白你的意思~~我的意思是,表达式
    id“hi there”
    id
    的类型是
    String->String
    ,因为
    “hi there”
    有类型String,所以
    a
    被实例化为。
    not
    的类型是
    Bool->Bool
    。那么表达式
    id not
    id
    的类型是什么?
    a
    也可以是一种类型,
    a->a
    中的
    a
    a->b->c
    中的
    a
    不同
    a->b->c
    a->(b->c)
    的缩写,因此如果你传递
    id::d->d
    ,那么我们知道
    a~d~b->c
    @user754458:如果我想让GHCi回答一个问题,比如“在表达式
    id not
    ”中,
    id的类型是什么,我可能会将其表述为
    :type\(\U::What)->(id::What)不是
    。由于
    what
    在这里是一个新的类型变量,它不会匹配任何具体类型,因此编译器将报告一个类型错误,说明
    what
    与我注释的子表达式的推断类型不匹配,并将在错误消息中为我打印该类型。这将使用“作用域类型变量”语言标志(
    :set-XScopedTypeVariables
    )。@user754458为什么不使用?这就是重点。在f实例中,通过传递它的id,您可以向您喜欢的类型添加任何限制,因此您可以将a限制为b->c@user754458为了匹配类型,编译器求解方程
    a->b->c=x->x
    ,并发现
    x=a=(b->c)
    。次要术语注意:这里没有自由(类型)变量。绑定是隐式的,但是语言规范明确了在没有绑定时如何插入绑定。显式版本,
    id::forall a。a->a
    f::对于所有a b c。(a->b->c)->字符串
    ,问题中的签名是简写的,这使得所有变量都不是自由变量更加明显。@user1937198否,自由和未绑定的含义相同。更符合文献的措辞应该是“因为你可以实例化变量”。我想不出任何相关的、正确的形容词附加到“变量”上。@DanielWagner如果措辞可以实例化,那么可以实例化还是不实例化?非常感谢。回答得好!
    id :: (b -> c) -> b -> c
    f :: ((b -> c) -> b -> c) -> String