Haskell:为什么约定将辅助函数命名为“ go”?


83

go在阅读Haskell的材料或资料时,我会看到很多东西,但是我从未真正感到满意-(我想它在我的脑海中具有“ goto”的负面含义)。我开始学习Haskell与LYAH,而且在那里我拿起使用倾向的accstep当写倍。书写惯例go从何而来?

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


4
我通常loop改为调用我的函数。
2011

2
go我读过的任何Haskell资料都从未见过。你能举个例子/参考吗?
Ionuț G. Stan

@ Ionuț例如,Yesod本书对Enumerator软件包的说明。(为什么Yesod的书为我不知道的主题提供了如此多的材料,但这并不重要)
Dan Burton

值得一提的是,我见过许多C / C ++程序员,当他们想到更好的名称时,也会将其辅助函数命名为“ go”。
ShreevatsaR 2011年

FWIW,明确尾递归后藤在很多方面,包括混淆的潜在功能的版本。但是,静态的类型和作用域规则有助于将混乱降至最低。至于名称的选择,我喜欢@Michael Snoyman在下面有关长度和适用性的答案。另外,当只有一个辅助函数时,它的名称似乎无关紧要,所以我通常只选择'go'或'loop',因为我必须选择一些东西,并且两者都有意义。对于惰性循环,我倾向于使用“ go”;对于严格的循环,我倾向于使用“ loop”。
mokus 2011年

Answers:


137

嗯!一些考古!

从2004年左右开始go,当对递归函数进行worker / wrapper转换时,我一直将其用作尾递归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 #-}

是从bytestring2005年8月开始的。

这是用RWH编写的,并且可能从那里开始流行。另外,在流融合库中,我和Duncan Coutts开始做很多事情。

来自GHC来源

这个习语可以追溯到更远。foldr在GHC.Base中给出为:

foldr k z = go
      where
         go []     = z
         go (y:ys) = y `k` go ys

这可能是我找到窍门的地方(我以为这是来自安迪·吉尔(Andy Gill)的论文,但在go那里找不到任何用处)。它在Gofer中没有以这种形式给出,所以我认为这首先出现在GHC代码库中。

到2001年,Simon Marlowgo在某些系统级代码中使用了,所以我们可能会将责任归咎于GHC,而这一线索使我们找到了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成语随处可见。例如,在Numeric.lhs的定义中showInt,日期为1996-1997:

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'
      }}

与H98报告中给出的实现不同。深入研究“ Numeric.lhs”的实现,我们发现它与1997年添加到GHC 2.06的版本不同,1998年4月,Sigbjorne Finne出现了一个非常有趣的补丁,添加了一个go循环到Numeric.lhs。

这表示至少到1998年,Sigbjorne才向goGHC“ std”库中添加了循环,而同时,GHC编译器内核中的许多模块都有go循环。进一步挖掘,Will Partain于1996年7月提交的这一非常有趣的内容为GHC添加了一个“ go”循环-该代码来自Simon PJ!

因此,我将其称为格拉斯哥成语,由格拉斯哥的人在90年代中期从事GHC工作时发明,例如Simon MarlowSigbjorn FinneWill PartainSimon Peyton Jones


4
+1是“尾递归工作循环的通用名称”,对于我所见过的大多数用途来说,这似乎通常是正确的。对于函数f,我个人通常会使用f'此类名称作为名称,尽管go我可能会尝试使用某种近似关键字的习惯用法。有趣的是,showInt使用惯用法避免多次评估同一个后卫。
丹·伯顿

1
顺便说一句,“这个名字到底意味着什么?” 我想说它暗示了goto,并将控制权移交给了辅助函数。
唐·斯图尔特

25
我模糊的回忆是,这是西蒙的PJ主义。我倾向于使用loop除非我要修改已经使用go约定的代码。我一直以为它的字面意思是“走”,就像“绕圈走”。
西蒙·马洛

5
我一直认为“ go”是对工作者函数进行肮脏的递归工作的命令。无论如何,就我个人而言,我是从其中一个流融合幻灯片中挑选出来的,因为在函数名称中添加滴答声总是会导致我忘记滴答声的问题。
海因里希·阿普菲尔姆斯

4
我相信它的起源早于Haskell。走的是一个流行的名字命名的方案让(google.com/...google.com/search?q=scheme+%22let+go%22+-let's+car+cdr
AtnNn

17

显然唐的答案是正确的。让我仅添加一个小细节(因为您直接引用的似乎是我的著作):go很不错,因为它只有两个字母。

噢,Yesod的书为枚举程序包投入了如此多的内容的原因是,因为我已经将枚举程序的教程分为三部分,作为博客文章系列,所以决定我也应该将其包含在书中。在Yesod的许多地方都使用了枚举程序包,因此它是相关的。


6
+1“ go”仅2个字母(并且仍然有意义),这是一个容易被低估的事实。当我评论Yesod书中对“ go”的使用(恕我直言,这是这些示例的极佳名称选择)时,我实际上正在阅读一个StackOverflow答案,当我觉得应该问这个问题时,便使用“ go”。不过,我立即想起了Yesod的书籍示例,因为它令人难忘。好东西!
丹·伯顿

11

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

我想知道go模式多久对应一次累积参数,更普遍地说,是与Mitch Wand在论文《基于持续的程序转换策略》(我最喜欢的论文之一)中探讨的连续编码策略相对应的。在这些情况下,go函数具有特殊含义,然后可以使用该含义从优雅的规范中得出有效的代码。


我认为值得一提的是,通常很难为这些函数起个好名字,原因很简单,因为它们通常是指封闭范围内的变量,因此并不完整。描述性名称可能看起来很愚蠢:类似add_xconsOnto_xs
dfeuer 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.