我将以下Haskell代码编译为核心:
class FunClass a where
functionInClass :: a -> ()
data MyData = MyData
data YourData = YourData
instance FunClass MyData where
functionInClass a = ()
instance FunClass YourData where
functionInClass a = ()
valueA :: ()
valueA = functionInClass MyData
valueB :: ()
valueB = functionInClass YourData
并获得以下核心绑定(我删除了一些不相关的样板):
$cfunctionInClass :: MyData -> ()
[LclId]
$cfunctionInClass = \ _ [Occ=Dead] -> break<3>() ()
$fFunClassMyData [InlPrag=INLINE (sat-args=0)] :: FunClass MyData
$fFunClassMyData
= $cfunctionInClass
`cast` (Sym (N:FunClass[0] <MyData>_N)
:: Coercible (MyData -> ()) (FunClass MyData))
$cfunctionInClass :: YourData -> ()
[LclId]
$cfunctionInClass = \ _ [Occ=Dead] -> break<2>() ()
$fFunClassYourData [InlPrag=INLINE (sat-args=0)] :: FunClass YourData
$fFunClassYourData
= $cfunctionInClass
`cast` (Sym (N:FunClass[0] <YourData>_N)
:: Coercible (YourData -> ()) (FunClass YourData))
valueA :: ()
[LclIdX]
valueA
= break<1>() functionInClass @ MyData $fFunClassMyData MyData
valueB :: ()
[LclIdX]
valueB
= break<0>()
functionInClass @ YourData $fFunClassYourData YourData
我的问题是:
两个cfunctionInClass
为什么共享相同的名称?我们如何区分他们?
cast
到底能做什么?
在mg_binds ModGuts
之外是否有任何与类型/实例有关的东西?
答案 0 :(得分:0)
在不知道(i)确切的GHC版本,(ii)使用的确切ghc
命令行以及(iii)您编译的文件的全部内容的情况下,很难复制您的核心输出正在询问,但是这里有一些答案:
1)生成内核时,您可能提供了标志-dsuppress-uniques
,使用了其他暗示它的标志,或者使用了默认版本的GHC的较早版本。此标志使GHC从核心输出中消除用于创建唯一名称的少量随机后缀。如果删除该标志或添加显式的-dno-suppress-uniques
,则应该看到诸如$cfunctionInClass_r1cH
和$cfunctionInClass_r1dh
之类的唯一名称。
2)Core是一种类型化的语言,函数cast
被(广泛地)用于改变表达式的类型。请注意,它不会更改表达式本身的内部表示形式,因此只能用于在内存中具有相同内部表示形式的类型之间进行切换。
对于使用newtypes
的代码,您会在各处进行强制转换。例如代码:
newtype MyInt = MyInt Int
inc (MyInt n) = MyInt (n + 1)
创建(未优化的)核心:
inc1 :: MyInt -> Int
inc1
= \ (ds :: MyInt) ->
+ @ Int $fNumInt (ds `cast` (N:MyInt[0] :: MyInt ~R# Int)) (I# 1#)
inc :: MyInt -> MyInt
inc
= inc1
`cast` (<MyInt>_R ->_R Sym (N:MyInt[0])
:: (MyInt -> Int) ~R# (MyInt -> MyInt))
有几个演员。
cast
的工作方式是,`cast`
运算符的左侧是一个普通的核心术语(例如变量或其他表达式),表示其类型正在改变的值;右侧是称为“强制”的东西,它是编译器构造的一种证据,用于证明两种类型在表示上是等效的(即,具有相同的内存表示,因此可以安全地强制)。例如,在上面的新类型示例中,第一次强制转换的强制是:
N:MyInt[0] :: MyInt ~R# Int
是强制值N:MyInt[0]
,其类型为~R#
和MyInt
的表示相等性(Int
)。 (从技术上讲,N:MyInt[0]
是强制类型 ,其种类是给定的表示性相等,但是这种区别并不重要。)如果您熟悉Curry-Howard同构,其中值可以视为其类型的证明,这是GHC胆识深处的一个实际例子-值/类型N:MyInt[0]
证明其类型/种类,即新类型及其内容,从而可以进行强制转换。
在您的示例中,演员:
$fFunClassMyData [InlPrag=INLINE (sat-args=0)] :: FunClass MyData
$fFunClassMyData
= $cfunctionInClass
`cast` (Sym (N:FunClass[0] <MyData>_N)
:: Coercible (MyData -> ()) (FunClass MyData))
是一种复杂的说法,GHC表示仅具有一个函数的类型类的实例字典,其表示方式与表示包含该类型的函数的新类型的表示方式相同,与表示函数值本身的表示方式相同。因此,可以将函数值$cfunctionInClass
直接转换为字典值。
但是,如果您向类型类添加了另一个函数:
class FunClass a where
functionInClass :: a -> ()
anotherFunction :: a
演员表的定义将从字典的定义中消失,它们看起来更像您所期望的:
$fFunClassMyData
$fFunClassMyData = C:FunClass $cfunctionInClass $canotherFunction
请务必注意,cast
在最终代码中没有执行任何操作。一旦将内核转换为无类型的STG并最终转换为CMM和程序集,cast
调用就会被优化,因为它们不影响值,它们仅修改编译时类型以满足内核类型检查器。因此,除非您正在调试GHC,否则您可能根本不在乎强制转换,因此应考虑不操作。您可以使用-dsuppress-coercions
(由-dsuppress-all
表示)隐藏一些细节:
$fFunClassYourData = $cfunctionInClass1 `cast` <Co:3>
并假装x `cast` <Co:xxx>
与x
完全等效。在上面的示例中,字典是只是类型类的单个实例函数,因此,与强制类型一样,这实际上与以下内容相同:
$fFunClassMyData = $cFunctionInClass
3)好的。其他类和实例信息分别存储在mg_tcs
的{{1}}和mg_insts
字段中。粗略估计,ModGuts
包含代码生成所需的信息,而mg_binds
和mg_tcs
包含生成接口文件所需的信息。
mg_insts
-漂亮打印核心模块。如果您想知道核心产品的来源,那就是它。 (例如,ghc/compiler/coreSyn/PprCore.hs
是负责漂亮地打印ppr_expr add_par (Cast expr co) = ...
运算符的代码。
`cast`
-ghc/compiler/coreSyn/CoreSyn.hs
类型是核心的“核心”。构造函数Expr
代表演员。
Cast (Expr b) Coercion
-ghc/compiler/types/TycoRep.hs
的定义在这里。
Coercion
-ghc/compiler/main/HscTypes.hs
的定义以及字段ModGuts
的“子集”用于代码生成,字段CgGuts
/ ModIface
用于编写接口文件和链接。
ModDetails
-函数ghc/compiler/main/TidyPgm.hs
的定义,其中tidyGuts
信息被分为ModGuts
用于代码生成和CgGuts
,{{的缓存版本1}}在编译多个模块时保留在内存中和/或用于生成完整的ModDetails
以写出到接口文件中。