在haskell报告98中,我们有以下定义:
https://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1360006.4.1
6.4.1数字文字第2.5节给出了数字文字的语法。整型文字表示函数fromInteger应用于integer类型的适当值。类似地,浮动文字代表将fromRational应用于Rational类型的值(即Ratio Integer)。给定打字员:
fromInteger::(Num a)=>整数->
fromRational::(分数a)=>理性->
整数和浮动文字的打字法(Num a)=>a和(分数a)=>a、 分别。
数字文字是以这种间接方式定义的,因此它们可以被解释为任何适当数字类型的值。有关过载模糊性的讨论,请参见第4.3.4节。
我完全理解规范的意图和目的。然而,出于好奇,我想知道";代表";以及";支架";。这就是在哪里以及如何实现文字";站立/代表">来自Integer和来自Rational。它是在GHC深处的某个地方,还是站在一个我们可以很容易地看到它的地方?
对于GHC来说,它实际上有点复杂。这确实发生在GHC编译器代码的深处,而不是因为你在其中一个base
模块中找到了一些定义,所以如果你只想知道这些,那就有答案了。以下是一些详细信息。。。
简短回答:GHC注意到2
可能需要在很早的时候通过fromInteger
来计算;重命名";解析后的阶段。然而,由重命名器输出的抽象语法树中2
的表示是可清楚识别的";文字";节点。随后在";类型检查阶段";,可以将一个潜在的替换表达式添加到该"替换"表达式中;文字";节点,并且这个替换可能是fromIntegral (2 :: Integer)
,但它也可能是对其最终类型中的文字进行更直接的编码。在任何情况下,该节点都保持为可清楚识别的"节点";文字";节点。字面节点的实际翻译仅在";减额";当AST转换为Core时,desugarer要么使用类型检查器生成的咨询表达式,要么完全忽略它,直接解析文本。总之,2
是从不无条件的"定义的";作为fromInteger (2 :: Integer)
,但最终通过重命名器、类型检查器和去加糖器之间的复杂相互作用被去加糖到该表达或适当的替代物。
长答案:如果您编译以下程序:
module NumericLiteral where
double :: Double -> Double
double x = 2 * x
使用ghc -ddump-parsed-ast -fforce-recomp Double.hs
在解析后但在重命名和搜索输出之前转储编译器的抽象语法树,您将找到一个HsOverLit
(用于"重载文本")节点。去掉额外的注释后,它看起来像:
HsOverLit (OverLit (HsIntegral 2) (HsLit (HsString "noExpr")))
最后一个字段是";见证人";对于重载的文字,这里的值是一个占位符。如果在使用ghc -ddump-rn-ast -fforce-recomp Double.hs
重命名后转储AST,您会发现更新后的节点现在具有以下形式:
HsOverLit (OverLit (HsIntegral 2)
(HsVar (Name "fromInteger")))
因此,函数fromInteger
很早就被引入,但其表示形式与函数调用fromInteger (2 :: Integer)
不同。这将是一个HsApp
功能应用程序节点。重命名器参与该过程的主要原因是为了支持CCD_ 14扩展;正确名称";从一开始。
稍后在类型检查过程中,对该节点进行了进一步的阐述。对于上面的程序,如果您使用ghc -ddump-tc-ast -fforce-recomp Double.hs
转储类型检查器输出,您将得到如下结果(使用8.2.2;GHC的较新版本会产生具有多个HsOverLit
节点的更复杂的AST):
HsOverLit (OverLit (HsIntegral 2)
(HsApp (HsWrap <coercion_evidence> (Var "fromInteger"))
(HsLit (HsInteger 2)))
在这一点上,应用程序fromInteger (2 :: Integer)
现在是可见的,但它仍然是";见证人";领域因此,在这个阶段,说原始文字已经被fromInteger (2 :: Integer)
替换为是不准确的,但GHC已经做了一种注释,即文字稍后可以被这个表达式替换。
然而,类型检查器并不总是以这种方式来解析节点。如果它有足够的本地信息,它有时会完全忽略重命名器提供的见证(fromInteger
),并用不同的见证表达式重写节点。例如,修改后的程序:
module NumericLiteral where
double :: Double -> Double
double x = (2 :: Double) * x
在解析和重命名后具有相同的HsOverLit
表示,但在类型检查后,节点看起来像:
HsOverLit (OverLit (HsIntegral 2)
(HsApp (HConLikeOut <boxed double contructor>)
(HsLit (HsDoublePrim (FL "2" (:% 2 1))))))
同样,这仍然清楚地是HsOverLit
节点,并且类型检查器见证应该被认为是"CCD_22"节点;"可能的替换";而不是实际的替换。
这是因为,无论类型检查器计算哪一个见证表达式,节点的转换都是由desugarer说了算。compiler/GHC/HsToCore/Match/Literal.hs
中的函数dsOverLit
负责:
dsOverLit :: HsOverLit GhcTc -> DsM CoreExpr
dsOverLit (OverLit { ol_val = val, ol_ext = OverLitTc rebindable ty
, ol_witness = witness }) = do
dflags <- getDynFlags
let platform = targetPlatform dflags
case shortCutLit platform val ty of
Just expr | not rebindable -> dsExpr expr -- Note [Literal short cut]
_ -> dsExpr witness
请注意,它首先应用来自异想天开命名的compiler/GHC/Tc/Utils/Zonk.hs
模块的shortCutLit
:
shortCutLit :: Platform -> OverLitVal -> TcType -> Maybe (HsExpr GhcTcId)
shortCutLit platform (HsIntegral int@(IL src neg i)) ty
| isIntTy ty && platformInIntRange platform i = Just (HsLit noExtField (HsInt noExtField int))
| isWordTy ty && platformInWordRange platform i = Just (mkLit wordDataCon (HsWordPrim src i))
| isIntegerTy ty = Just (HsLit noExtField (HsInteger src i ty))
| otherwise = shortCutLit platform (HsFractional (integralFractionalLit neg i)) ty
-- plus more cases for `HsFractional` and `HsIsString` literals
如果shortCutLit
标识了一个特殊情况,则重命名器和类型检查器生成的见证将被忽略,并且该文本将直接降级为Core中的文本。只有当shortCutLit
失败时,desugarer才会在desugared输出中使用类型检查器的见证。
因此,总而言之,重命名器识别了一个(可能反弹)fromInteger
函数,该函数可能与2
的降级有关。类型检查器选择一个";见证人";表达式,fromInteger (2 :: Integer)
或一些更直接的翻译(如果有足够的本地信息可用),并且具有更多可用类型信息的desugarer对清楚可识别的文本2
节点的翻译做出最终决定,将其翻译为类型检查器的见证(fromInteger (2 :: Integer)
或更直接的东西)或忽略类型检查器的证明并执行其自己的直接翻译。
我认为您对这个定义的精确措辞读得太多了。这意味着像1
这样的整数字面实际上被解释为fromInteger (1 :: Integer)
;类似地,像CCD_ 36这样的浮动文字实际上被解释为CCD_。这是允许数值文本在Haskell中多态的机制。