Prelude> :i ($)
($) ::
forall (r :: GHC.Types.RuntimeRep) a (b :: TYPE r).
(a -> b) -> a -> b
-- Defined in ‘GHC.Base’
infixr 0 $
与
(a -> b) -> a -> b
有何不同?是否有不适合新类型签名的b
? 最佳答案
在8.0之前的版本中,类型检查器中有一种特殊情况,可以将$
的应用程序应用于未提升的种类。这也意味着您无法定义自己的函数,该函数既可以用于提升类型也可以用于非提升类型。现在已经在类型检查器中内置了所谓的Levity Polymorphsim(“高度”是指“某物被举起的程度”或“提起度”,因为“未举起”和“举起”类型)。 :
import GHC.Exts (TYPE, RuntimeRep(..))
import Data.Kind (type (*))
ap :: forall (a :: *) (b :: *) . (a -> b) -> (a -> b)
ap f x = f x
ap_LP :: forall (a :: *) (b :: TYPE r) . (a -> b) -> (a -> b)
ap_LP f x = f x
实际上,现在
$
函数的定义与ap_LP
相同,而在typechecker中不需要特殊情况以使$
与返回未提升类型的函数一起工作(在typechecker中,仍然有一种特殊情况可以使多态应用程序生效,即runST $ ...
可以工作,但是这与高度多态性无关)。这本质上是增加了复杂性的原因-类型系统中的“hacks”现在越来越少,GHC的用户只需给函数提供适当的类型就可以利用levity多态性(请注意,从不推断levity-polymorphic类型,据我所知)。在使用高度多态性之前,如果您想编写一个对提升类型和非提升类型都可能起作用的多态函数,则必须编写具有不同类型签名的函数的两个相同副本。新类型与旧类型的不同之处在于,新类型比旧类型严格地更通用:
-- ok
ap' :: forall (a :: *) (b :: *) . (a -> b) -> (a -> b)
ap' = ap_LP
-- type error:
-- * Couldn't match a lifted type with an unlifted type
ap_LP' :: forall (a :: *) (b :: TYPE r) . (a -> b) -> (a -> b)
ap_LP' = ap
换句话说,每个“适合”旧签名的
b
必须(根据定义)必须适合新类型的签名(因此,此更改是完全向后兼容的!)。另请注意,以下操作是不可能的:
ap'' :: forall (a :: TYPE r) (b :: *) . (a -> b) -> (a -> b)
ap'' f x = f x
产生的错误是
A representation-polymorphic type is not allowed here:
Type: a
Kind: TYPE r
In the type of binder `x'
SPJ解释了限制here的原因:
这就是说,并非每个levity-polymorphic类型都有一个有效的居住者-这与未装箱类型和装箱类型之间的操作区别有关,只能在某些情况下进行统一处理,并且类型检查器会对此加以确认。
关于haskell - 为什么在GHC 8.0.1中美元($)运算符如此复杂?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39186288/