jsm*_*mrt 1 haskell types ghci
在测试中,我被要求推断出以下类型:
let pr = map head.group.sortBy(flip compare)
Run Code Online (Sandbox Code Playgroud)
我自己推断出类型是:
Ord a => [a] -> [a]
Run Code Online (Sandbox Code Playgroud)
但是,当:t在GHCi中进行时,它表示类型是:
pr :: [()] -> [()]
Run Code Online (Sandbox Code Playgroud)
到底是怎么回事?
如果在GHCi我也这样做:
map head.group.sortBy(flip compare) [1,2,3,4,100,50,30,25,51,70,61]
Run Code Online (Sandbox Code Playgroud)
我收到一个错误:
Couldn't match expected type `a0 -> [b0]' with actual type `[a1]'
In the return type of a call of `sortBy'
Probable cause: `sortBy' is applied to too many arguments
In the second argument of `(.)', namely
`sortBy (flip compare) [1, 2, 3, 4, ....]'
In the second argument of `(.)', namely
`group . sortBy (flip compare) [1, 2, 3, 4, ....]'
Run Code Online (Sandbox Code Playgroud)
但是,如果我这样做:
sortBy(flip compare) [1,2,3,4,100,50,30,25,51,70,61]
[100,70,61,51,50,30,25,4,3,2,1]
Run Code Online (Sandbox Code Playgroud)
它工作得很好.当第二个表达式sortBy使用完全相同的参数进行评估时,为什么第一个表达式失败?
你的第一个问题是单变量限制的可怕组合,GHCi无法立即看到你的整个程序,以及GHCi扩展的默认规则.
简而言之,Haskell不喜欢使用多态类型类约束(Ord a =>类型签名的一部分)来推断顶级绑定的类型,这些顶级绑定被编写为语法上没有参数的方程式.pr = map head.group.sortBy(flip compare)违反了这个规则(它是一个函数,所以在语义上它有参数,但你用来定义它的方程式没有),所以Haskell希望Ord约束a是具体的东西.
如果你把它放在一个源文件中并编译它(即使是通过GHCi):
import Data.List
pr = map head.group.sortBy(flip compare)
Run Code Online (Sandbox Code Playgroud)
你会得到彻头彻尾的错误,例如:
foo.hs:3:33:
No instance for (Ord b0) arising from a use of `compare'
The type variable `b0' is ambiguous
Possible cause: the monomorphism restriction applied to the following:
pr :: [b0] -> [b0] (bound at foo.hs:3:1)
Probable fix: give these definition(s) an explicit type signature
or use -XNoMonomorphismRestriction
Note: there are several potential instances:
instance Integral a => Ord (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
instance Ord () -- Defined in `GHC.Classes'
instance (Ord a, Ord b) => Ord (a, b) -- Defined in `GHC.Classes'
...plus 22 others
In the first argument of `flip', namely `compare'
In the first argument of `sortBy', namely `(flip compare)'
In the second argument of `(.)', namely `sortBy (flip compare)'
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)
对于某些类型(特别是数字类型),这种"模糊类型变量"错误会出现很多并且会令人恼火,因此Haskell有一些默认规则.例如,它将假设一个模糊的类型变量仅由Num应该约束Integer.当然,如果您在同一个文件中的任何位置使用该函数,如下所示:
import Data.List
pr = map head.group.sortBy(flip compare)
answer = pr [1,2,3,4,100,50,30,25,51,70,61]
Run Code Online (Sandbox Code Playgroud)
那么Haskell可以考虑到.它仍然拒绝推断出多态类型pr,但是在这种情况下pr它只是像它一样被使用[Integer] -> [Integer],所以它会给它那个类型并允许你的代码编译,而不是发出模糊的类型变量错误(它Integer本身就是也是类型默认的结果).
在GHCI,你的代码在编译时一个说法,所以也没有考虑到你使用的pr,以决定什么类型的给它.这将会给你一个暧昧的错误类型,除了GHCI已经扩展默认规则,这踢这里"拯救世界",让你的表达进行编译.通过违约Ord a => a类型可变的单元类型(),您的声明可以被解释为用于冷凝的任意列出了一个函数定义()成[()](或[]如果输入是空的).谢谢GHCi!
您可以通过几种不同的方式解决此问题.一种是在你的定义的两边添加一个参数pr,如下:
let pr z = map head.group.sortBy(flip compare) $ z
Run Code Online (Sandbox Code Playgroud)
现在,方程式定义pr具有一个参数的合成(它的类型/含义仍然具有相同数量的参数),单态约束不起作用,并且Haskell很乐意推断出多态类型pr.
另一种方法是通过添加{-# LANGUAGE NoMonomorphismRestriction #-}到模块顶部或:set -XNomonomorphismRestriction在GHCi提示符下使用来明确告诉它您不想使用Monomorphism Restriction .然后,它会再次推断类型Ord a => [a] -> [a]的pr.
第三种方法是明确给出函数的多态类型签名:
import Data.List
pr :: Ord a => [a] -> [a]
pr = map head.group.sortBy(flip compare)
Run Code Online (Sandbox Code Playgroud)
或者在GHCi中:
> let { pr :: Ord a => [a] -> [a] ; pr = map head.group.sortBy(flip compare) }
Run Code Online (Sandbox Code Playgroud)
因为即使在力哈斯克尔的单态的限制是高兴pr到具有多态类型,它只是将无法推断一个吧.
显式类型签名可能是人们在编译文件中避免此问题的最常见方式,因为许多人认为始终为顶级定义提供类型签名是一种好方式.正如你所看到的,在GHCi中,它非常烦人; 我通常会关闭那里的单态限制.
至于你的第二个问题,我担心这个:
map head.group.sortBy(flip compare) [1,2,3,4,100,50,30,25,51,70,61]
Run Code Online (Sandbox Code Playgroud)
与此截然不同:
pr [1,2,3,4,100,50,30,25,51,70,61]
Run Code Online (Sandbox Code Playgroud)
当你被pr定义为一个函数时,pr引用整个函数map head.group.sortBy(flip compare),所以给它一个参数提供一个参数给该函数.但是当你写出整个表达式时,只是将一个列表放在它的右边并不会将它作为一个参数传递给整个表达式.它解析得更像这样:
(map head) . (group) . (sortBy (flip compare) [1,2,3,4,100,50,30,25,51,70,61])
Run Code Online (Sandbox Code Playgroud)
正如你可以看到,名单内的管道的最后一个函数; sortBy (flip compare) [1,2,3,4,100,50,30,25,51,70,61]被用作函数,它将接受一个参数并通过管道(to group)进一步提供其输出.这显然没有意义,这就是为什么你会收到一条错误信息,抱怨说有太多的争论sortBy; 这并不是说你已经提供了太多的参数sortBy,而是您提供其所有参数,然后在一个位置,它必须能够采取一个更加使用它.
这有时可奇怪的,直到你习惯它,但任何替代更加频繁地奇(隐式地依赖于分析在使用这种方式工作map head和sortBy (flip compare)).你需要做的就是记住普通的函数应用程序(通过将两个表达式彼此相邻)总是优于中缀运算符(如.); 每当你有一个混合中缀运算符和普通应用程序的表达式时,每个正常的应用程序链(仅由空格分隔的非运算符表达式组)就中缀运算符而言只变成一个参数(然后优先级/关联性是用于解析中缀运算符的参数是什么).
要修复它,您需要在引入参数之前在合成管道周围添加括号,如下所示:
(map head.group.sortBy(flip compare)) [1,2,3,4,100,50,30,25,51,70,61]
Run Code Online (Sandbox Code Playgroud)
或者用于$在组合管道和参数之间放置一个"墙",如下所示:
map head.group.sortBy(flip compare) $ [1,2,3,4,100,50,30,25,51,70,61]
Run Code Online (Sandbox Code Playgroud)
这是因为$是另一个中缀运算符,所以它强制所有"正常应用"序列向左和向右解析,然后才能将一个应用到另一个.它也是一个非常低优先级的运算符,所以当其他中缀运算符也在运行时(例如.)它几乎总是有效.在Haskell中编写表单的表达式是一个很常见的习惯用法f . g . h $ a.