Haskell:为什么要使用命名助手函数的约定;围棋;?

Haskell:为什么要使用命名助手函数的约定;围棋;?,haskell,naming-conventions,Haskell,Naming Conventions,当我阅读哈斯克尔的材料或资料时,我看到了很多“go”,但我对它从来都不是很满意(我想它在我脑海中有“goto”的负面含义)。我开始与LYAH一起学习Haskell,在那里我开始倾向于在编写折叠时使用acc和step。书写的惯例从何而来 最重要的是,go这个名字到底意味着什么?嗯!一些考古学 大约从2004年开始,在对递归函数进行worker/wrapper转换时,我使用了go作为尾部递归worker循环的通用名称。我开始在bytestring中广泛使用它,例如 foldr :: (Word8 -

当我阅读哈斯克尔的材料或资料时,我看到了很多“go”,但我对它从来都不是很满意(我想它在我脑海中有“goto”的负面含义)。我开始与LYAH一起学习Haskell,在那里我开始倾向于在编写折叠时使用
acc
step
。书写
的惯例从何而来

最重要的是,
go
这个名字到底意味着什么?

嗯!一些考古学

大约从2004年开始,在对递归函数进行worker/wrapper转换时,我使用了
go
作为尾部递归worker循环的通用名称。我开始在
bytestring
中广泛使用它,例如

foldr :: (Word8 -> a -> a) -> a -> ByteString -> a
foldr k v (PS x s l) = inlinePerformIO $ withForeignPtr x $ \ptr ->
        go v (ptr `plusPtr` (s+l-1)) (ptr `plusPtr` (s-1))
    where
        STRICT3(go)
        go z p q | p == q    = return z
                 | otherwise = do c  <- peek p
                                  go (c `k` z) (p `plusPtr` (-1)) q -- tail recursive
{-# INLINE foldr #-}
这可能就是我学会的窍门(我原以为这是安迪·吉尔的论文,但在那里找不到任何
go
的用法)。在Gofer中没有以这种形式给出,所以我认为它首先出现在GHC代码库中

到2001年,Simon Marlow在一些系统级代码中使用了
go
,因此我们可能会将责任归咎于GHC,这条线索引导我们,其中
go
广泛应用于工作函数:

myCollectBinders expr
  = go [] expr
  where
    go bs (Lam b e)          = go (b:bs) e
    go bs e@(Note (SCC _) _) = (reverse bs, e)
    go bs (Cast e _)         = go bs e
    go bs (Note _ e)         = go bs e
    go bs e                  = (reverse bs, e)
GHC 3.02和格拉斯哥

翻开GHC的旧版本,我们发现在GHC 0.29中没有出现这个成语,但在GHC 3.02系列(1998年)中,
go
成语随处可见。例如,在1996-1997年的
Numeric.lhs
中,在
showInt
的定义中:

showInt n r
  | n < 0     = error "Numeric.showInt: can't show negative numbers"
  | otherwise = go n r
    where
     go n r =
      case quotRem n 10 of                 { (n', d) ->
      case chr (ord_0 + fromIntegral d) of { C# c# -> -- stricter than necessary
      let
    r' = C# c# : r
      in
      if n' == 0 then r' else go n' r'
      }}
showInt n r
|n<0=错误“Numeric.showInt:无法显示负数”
|否则=继续
哪里
继续=
第10号案例{(n',d)->
{C#C#->的情形chr(ord#u 0+来自积分d)——比必要的更严格
让
r'=C#C#:r
在里面
如果n'==0,则r'否则为n'r'
}}
这是一个不同的实现。但是,深入研究的实现,我们发现它与1997年添加到GHC 2.06的版本不同,1998年4月,Sigbjorn Finne发布了一个非常有趣的补丁

这意味着至少到1998年,Sigbjorn将
go
循环添加到GHC“std”库中,同时,许多人拥有
go
循环。进一步挖掘,这个——代码来自Simon PJ


因此,我将其称为格拉斯哥习语,由格拉斯哥的人发明,他们在90年代中期从事GHC工作,例如,和。

我希望这个习语不仅适用于线性结构(因此也适用于“循环”),还适用于分支(树状)结构

我想知道
go
模式与累加参数对应的频率有多高,更一般地说,与米奇·旺德在论文(我一直最喜欢的论文之一)中探讨的连续编码策略对应的频率有多高。
在这些情况下,
go
函数有一个特殊的含义,可以用来从优雅的规范中导出有效的代码。

显然,Don的答案是正确的。让我补充一个小细节(因为你直接提到的似乎是我的作品):go很好,因为它只有两个字母


哦,Yesod的书之所以在enumerator软件包中投入这么多内容,是因为我已经把enumerator的三部分教程写成了一个博客文章系列,所以我决定把它也包括在书中。enumerator包在整个Yesod中的许多地方都使用,因此它是相关的。

我通常调用我的函数
loop
。在我阅读的任何Haskell材料中从未见过
go
。你能举个例子/参考吗?@Ionuț。(为什么这本书在这个主题上花了这么多的材料我不知道,但这是离题的)为了它的价值,我见过许多C/C++程序员在想不出更好的名字时也把他们的助手函数命名为“go”。FWIW,显式尾部递归在很多方面都是goto的函数版本,包括潜在的混淆。不过,静态类型和范围规则有助于将混淆降至最低。至于名字的选择,我喜欢下面@Michael Snoyman关于长度和适用性的回答。另外,当只有一个helper函数时,它的名称似乎基本上是不相关的,所以我通常只选择“go”或“loop”,因为我必须选择一些东西,它们都是有意义的。我倾向于用“go”表示惰性循环,用“loop”表示严格循环。+1表示“尾部递归工作循环的通用名称”,这在我见过的大多数用法中似乎都是正确的。对于函数
f
,我个人通常会使用
f'
作为这类事物的名称,尽管我可能会尝试使用
go
作为一种近似关键字的习惯用法。有趣的是,
showInt
使用这个成语来避免多次评估同一个保护。顺便说一句,对于“go的名字到底意味着什么?”我想说它暗示了
goto
,以及将控制权交给助手函数。我模糊的记得这是一个Simon PJ ism。我倾向于使用
loop
,除非我正在修改已经使用
go
约定的代码。我一直认为它的字面意思是“围着循环转”,我一直认为“围着循环转”是对worker函数的一个命令,以开始它肮脏的递归工作。在任何情况下,就我个人而言,我从一张流融合幻灯片中选择了它,因为在函数名中添加勾号总是有一个问题,那就是我会忘记勾号。我相信它起源于Haskell之前。go是scheme(,)中命名let的流行名称+1“go”只有2个字母(仍然有意义),这是一个很容易理解的事实。当我评论Yesod书中使用的“go”(这是一个很好的名字)时
showInt n r
  | n < 0     = error "Numeric.showInt: can't show negative numbers"
  | otherwise = go n r
    where
     go n r =
      case quotRem n 10 of                 { (n', d) ->
      case chr (ord_0 + fromIntegral d) of { C# c# -> -- stricter than necessary
      let
    r' = C# c# : r
      in
      if n' == 0 then r' else go n' r'
      }}