在 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) => 整數 -> a
fromRational :: (Fractional a) => Rational -> a
整數和浮點字面量的型別分別為 (Num a) => a 和 (Fractional a) => a。
數字文字以這種間接方式定義,因此它們可以被解釋為任何適當數字型別的值。有關多載歧義的討論,請參見第 4.3.4 節。
我完全理解規范的意圖和目的。但是出于好奇,我想知道“代表”和“立場”在哪里以及如何實施。這就是在哪里以及如何實作fromInteger和fromRational的文字“常設/表示”的定義。它是在 GHC 深處的某個地方,還是位于我們可以很容易看到的地方?
uj5u.com熱心網友回復:
對于 GHC,它實際上有點復雜。這確實發生在 GHC 編譯器代碼的深處,而不是因為您會在其中一個base模塊中找到一些定義,所以如果這就是您想知道的全部,那就是您的答案。這里有更多細節......
簡短回答: GHC 注意到2可能需要fromInteger在決議后的“重命名”階段很早就計算的事實。但是,2重命名器輸出的抽象語法樹中的表示是一個清晰可識別的“文字”節點。隨后在“型別檢查階段”,可能會向該“文字”節點添加一個潛在的替換運算式,并且該替換可能是fromIntegral (2 :: Integer),但它也可能是其最終型別中文字的更直接編碼。無論如何,該節點仍然是一個清晰可識別的“文字”節點。文位元組點的實際翻譯僅在 AST 轉換為 Core 時的“脫糖”階段完成,脫糖器要么使用型別檢查器生成的建議運算式,要么完全忽略它以支持直接決議文字。總之,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")))
最后一個欄位是多載文字的“見證”,這里的值是一個占位符。如果您在使用 重命名后轉儲 AST ghc -ddump-rn-ast -fforce-recomp Double.hs,您會發現更新后的節點現在具有以下形式:
HsOverLit (OverLit (HsIntegral 2)
(HsVar (Name "fromInteger")))
因此,該函式fromInteger很早就被引入,但表示形式與函式呼叫不同fromInteger (2 :: Integer)。那將是一個HsApp功能應用程式節點。重命名器參與此程序的主要原因是為了支持RebindableSyntax需要fromInteger從一開始就決議為“正確名稱”的擴展。
稍后在型別檢查期間,該節點被進一步細化。對于上面的程式,如果您使用 轉儲型別檢查器輸出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節點,型別檢查器見證應被視為“可能的替代品”,而不是實際的替代品。
這是因為,無論型別檢查器計算哪個見證運算式,脫糖器對節點的翻譯擁有最終決定權。dsOverLit中的函式compiler/GHC/HsToCore/Match/Literal.hs負責:
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
請注意,它首先適用shortCutLit于異想天開的compiler/GHC/Tc/Utils/Zonk.hs模塊:
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失敗時,脫糖器才會在脫糖輸出中使用型別檢查器的見證。
因此,總而言之,重命名器標識了一個可能fromInteger與去糖相關的(可能是反彈的)函式2。如果有足夠的本地資訊可用,型別檢查器會選擇“見證”運算式,或者選擇fromInteger (2 :: Integer)更直接的翻譯,并且脫糖器 - 具有更多可用的型別資訊 - 對清晰可識別的文字2節點的翻譯做出最終決定,要么將其翻譯成型別檢查器的見證(fromInteger (2 :: Integer)或更直接的東西)或忽略型別檢查器的見證并執行其自己的直接翻譯。
uj5u.com熱心網友回復:
我認為你對這個定義的精確措辭讀得太多了。它的意思是整數文字1真的被解釋為fromInteger (1 :: Integer); 同樣,浮動文字 like0.5確實被解釋為fromRational (0.5 :: Rational). 這是允許數字文字在 Haskell 中多型的機制。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/517147.html
標籤:哈斯克尔
