对GHC 4的贡献:实际问题
在过去的几周中,我们对GHC进行了很好的了解。 我们首先查看了为GHC开发准备本地机器所需的步骤。 在Windows上,这是一个特别困难的过程,因此我们专注于此。 之后,我们研究了为自己创建开发周期的基本方法。 我们通过更改错误消息并查看错误消息在编译器中的显示方式进行了验证。 上周 ,我们进行了一些更复杂的更改。 本周,我们将通过研究一些基本的贡献方式来总结本系列。
文献资料
在任何软件项目中,文档编制都是一件棘手的事情。 在任何给定的时刻,大部分工作都在确保程序按预期运行。 当您已经了解了代码后,就无需查看文档。 因此,诱惑是不要更改任何评论。 这意味着文档总是很可能过期。 Haskell(如果有的话)更容易发生这种失误。 我们通过进行更改,编译和查看中断来寻找问题。 文档永远不会中断!
经验丰富的开发人员将记住更多地更改文档。 尽管如此,仍然不可避免地会出现一些裂缝。 但是,作为GHC代码库的新手,对我们来说是个好消息! 因为我们是最需要阅读文档的人,所以我们最有能力在文档中找到漏洞! 这就是我找到我能做出的第一贡献的方式。
在探索词法类型时,我发现一条注释不太有意义。 在compiler/basicTypes/BasicTypes.hs
的顶部,它指出:
-- There is considerable overlap between the logic here and the logic
-- in Lexer.x, but sadly there seems to be way to merge them.
那不是很正确。 从上下文来看,作者似乎很想写“似乎没有办法将它们合并”。 太好了,让我们为此提交一个拉取请求! 我们将分叉存储库并打开请求请求。 因此,我们将创建我们的fork ,克隆存储库,打开一个新分支,并针对master打开一个拉取请求。
现在有一个令人讨厌的问题,那就是CI的构建现在似乎并没有通过。 但希望此公关会在某个时候合并。
Trac的问题跟踪
当然,GHC还面临着更为复杂的问题。 我们要添加到代码库中的实际功能以及我们要修复的错误! 要查看那里发生了什么,您需要查看问题跟踪器 。 GHC为此使用Trac,您可以观察到该列表上的所有问题。 他们根据其所针对的版本及其重要性来确定标签。
这可能是一个压倒性的清单。 我滚动浏览了许多不同的票证,但不确定我实际上可以提供哪些帮助。 那么,如何找到一些开始的东西呢? 首先,您可以订阅GHC开发者邮件列表。 那里的对话将帮助您找到人们正在从事的工作。 其次,您可以登录Freenode并进入#ghc
频道。 您可以向任何人询问发生了什么事,以及在什么地方可以提供帮助。 幸运的是,问题列表上还有一个“新来者”标签。 对于刚接触代码库的人来说,GHC开发人员强调的这些问题应该很容易。 让我们看一下其中一个问题。
看一个真正的问题:中缀模式
通过这次搜寻,我发现了与(->)
缀值有关的票证 。 票证声称箭头操作符的规定的0的插入级别实际上是不正确的。 让我们看看它们的含义。
提醒一下,在确定操作顺序时,中缀级别说明操作员的优先级。 例如,乘法运算符(*)
具有比加法运算符(+)
高的中缀级别。 我们可以通过对每个命令使用:info
命令,通过快速的ghci会话来确认此信息。
>> :i (+)
…
infixl 6 +
>> :i (*)
…
infixl 7 *
>> 5 + 2 * 3
11 -- Would be 21 if addition were higher precedence
现在,当两个运算符具有相同的中缀级别时,我们将参考中缀级别的方向。 例如,我们可以将减法与加法进行比较。 我们还会发现它也是infixl 6
。 由于它是infixl
(而不是infixr
),因此我们优先考虑左侧操作。 这是一个例子。
>> :i (-)
…
infixl 6 -
>> 5 - 2 + 18
21 -- Not (-15)
因此,让我们看一下定义类型签名时使用的箭头运算符:
>> :i (->)
data (->) (a :: TYPE q) (b :: TYPE r) -- Defined . `GHC.Prim`
infixr 0 `(->)`
...
这表明此运算符的中缀级别为0,因此我们应该优先处理右边的事情。 但是,提交此错误的人员建议以下代码:
{-# LANGUAGE TypeOperators #-}
module Bug where
import Data.Type.Equality
type (~>) = (->)
infixr 0 ~>
f :: (a ~> b -> c) :~: (a ~> (b -> c))
f = Refl
这里有很多更高层次的概念,因此让我们将其分解。 首先, (->)
是类型运算符,这意味着它本身实际上是类型。 因此,我们可以为其创建一个名为(~>)
的类型同义词。 然后,我们可以将这个新运算符分配给我们想要的任何中缀级别。 在这种情况下,我们将选择与原始运算符infixr 0
相同的声明中缀级别。
下一部分创建一个表达式f
。 其类型签名使用(:~:)
运算符来实现类型之间的关系相等。 此类型具有Refl
构造函数。 您唯一需要了解的是我们的每个箭头模式( (a ~> b -> c)
和(a ~> (b -> c))
)都是一种类型。 并且仅当这些类型相同时, 才应编译此代码。
从表面上看,这些类型应该相同。 毕竟,两个运算符都声称是infixr 0
,这意味着我们在(:~:)
右边加上括号的方式应该与自然排序的方式匹配。 但是代码无法编译!
>> ghci
>> :l Bug.hs
Bug.hs:11:5: error:
* Couldn’t match type `a` with `a ~> b`
`a` is a rigid type variable bound by
f :: forall a b c. ((a ~> b) -> c) :~: (a ~> ( b -> c))
At Bug.hs:10:1-38
Expected type: ((a ~> b) -> c) :~: (a ~> (b -> c))
Actual type: ((a ~> b) -> c) :~: ((a ~> b) -> c)
* In the expression: Refl
In an equation for `f’: f = Refl
* Relevant bindings include
f :: ((a ~> b) -> c) :~: (a ~> (b -> c))
(bound at Bug.hs:11:1)
|
11 | f = Refl
|
我们可以在“实际类型”行上看到编译器如何解释(a ~> b -> c)
。 它优先于左侧,而不是右侧。 实际上,如果我们更改类型签名以反映给(~>)
优先级,我们的代码将编译:
f :: (a ~> b -> c) :~: ((a ~> b) -> c)
f = Refl
…
>> ghci
>> :l Bug.hs
Ok, one module loaded.
修复
幸运的是,已经在票证中提出了解决方案。 编译器使用Fixity
类型表示操作员的中缀级别。 我们可以看到一个特定的位置,在其中定义了一些内置运算符的级别:
negateFixity, funTyFixity :: Fixity
negateFixity = Fixity NoSourceText 6 InfixL -- Fixity of unary negate
funTyFixity = Fixity NoSourceText 0 InfixR -- Fixity of `->`
我们要更改函数类型运算符的固定性。 我们应该使它看起来为-1
,而不是看起来像0,而是使它看起来-1
。 请注意,此代码是指我们向我们报告的代码。 它最终具有较低优先级的实际原因更加复杂。 但是,让我们进行更改:
funTyFixity = Fixity NoSourceText (-1) InfixR
测试我们的变化
似乎应该对测试进行简单的更改。 首先,我们需要make
一次我们的代码。 然后,我们将启动GHCI并询问有关(->)
。 但这在我们尝试时似乎无效!
> make
> ghci
...
>> :i (->)
data (->) (a :: TYPE q) (b :: TYPE r) -- Defined . `GHC.Prim`
infixr 0 `(->)`
...
这里的问题是,重新制作不会导致GHCI使用我们在本地构建的新版本的GHC。 即使从ghc/inplace/bin
ghci.exe
ghc/inplace/bin
目录中使用ghci.exe
,它仍然不能解决此更改。 解决此问题的方法是,可以不使用ghci
而是将--interactive
标志传递给ghc
的常规调用。 所以我们想要这样的东西:
~/ghc/inplace/bin/ghc-stage2.exe -o prog --interactive Main.hs
这将显示GHCI提示,该提示会加载我们的主模块。 现在,当我们继续获取信息时,我们将看到它有效!
> ~/ghc/inplace/bin/ghc-stage2.exe -o prog --interactive Main.hs
...
>> :i (->)
data (->) (a :: TYPE q) (b :: TYPE r) -- Defined . `GHC.Prim`
infixr -1 `(->)`
...
因此,我现在将提出一个简单的请求来解决此错误。 您可以在此处关注进度。 在此过程中,我将对其进行更新。
结论
这总结了我们为GHC做贡献的系列! 那里有很多错误,所以不要害怕看看标记为newcomer
任何东西。 只需确保查看票证上已经发生的讨论即可!
要了解有关Haskell的更多信息,如果您已经熟悉该语言,则可以阅读我们的Liftoff系列 (面向初学者)或Haskell Web系列 。 您也可以下载我们的Haskell初学者清单以开始使用! 如果您需要一些更高级的项目的想法,也可以查看我们的生产清单 。
From: https://hackernoon.com/contributing-to-ghc-4-real-issues-8238fba87775
上一篇: requirejs模块化开发后的合并压缩
下一篇: js简单的面试题