Haskell:なぜヘルパー関数に「go」という名前を付けるのですか?


83

goHaskellの資料や情報源を読んでいるとよく見かけますが、それについて本当に快適に感じることはありませんでした-(私の心の中には「goto」という否定的な意味合いがあると思います)。私はLYAHでHaskellのを学び始め、そして私が使用する傾向を拾いのことaccstepするときは、折り目を書きます。書くための慣習はどこgoから来たのですか?

最も重要なことは、その名前が正確に何goを意味するのでしょうか?


4
私は通常、loop代わりに関数を呼び出します。
2011

2
go私が読んだHaskellの資料では見たことがありません。例/参考資料を教えてください。
Ionuț G. Stan 2011

@ Ionuțたとえば、Yesod本の列挙子パッケージの説明。(なぜイェソドの本は私が知らないトピックに多くの資料を捧げているのですか、しかしそれは重要ではありません)
ダンバートン

価値のあることとして、私は多くのC / C ++プログラマーが、より良い名前を思いつかないときにヘルパー関数に「go」という名前を付けるのを見てきました。
ShreevatsaR

FWIW、明示的な末尾再帰、難読化の可能性を含め、多くの点でgotoの機能バージョンです。ただし、静的な型指定とスコープのルールは、混乱を最小限に抑えるのに役立ちます。そして、名前の選択に関しては、長さと適合性についての以下の@MichaelSnoymanの回答が好きです。また、ヘルパー関数が1つしかない場合、その名前はほとんど関係がないように思われるため、何かを選択する必要があり、どちらも意味があるため、通常は「go」または「loop」のいずれかを選択します。私は怠惰なループには「go」を、厳密なループには「loop」を好む傾向があります。
mokus 2011年

回答:


137

うーん!いくつかの考古学!

2004年頃からgo、再帰関数のワーカー/ラッパー変換を行うときに、末尾再帰ワーカーループの総称として使用してきました。私はそれを広く使い始めました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で書かれおそらくそこから普及したのでしょう。また、ストリームフュージョンではライブラリでは、DuncanCouttsと私はそれをたくさんやり始めました。

GHCソースから

しかし、イディオムはさらに遡ります。foldrGHC.Baseでは次のように与えられます:

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

これはおそらく私がトリックを拾った場所です(これはAndy Gillの論文からのものだと思いましたが、goそこでの使用法は見つかりませんでした)。そうではありませんGoferにこの形式で与えられたので、私はこれが最初のGHCのコードベースに登場だと思います。

2001年、サイモン・マーローは使用していたgoシステムレベルのコードの一部では、私たちは、に私たちを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)

GHC3.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月にSigbjorneFinneからの非常に興味深いパッチが追加されました。goNumeric.lhsにループします。

これは、少なくとも1998年までに、SigbjorneがgoGHCの「std」ライブラリにループを追加し、同時にGHCコンパイラコアの多くのモジュールにgoループがあったことを示しています。さらに掘り下げてみると、1996年7月のWill Partainによるこの非常に興味深いコミットにより、GHCに「go」ループが追加されます。コードはSimonPJからのものです。

だから私はこれをサイモン・マーロウシグビョルン・フィンウィル・パーテンサイモン・ペイトン・ジョーンズなど、90年代半ばにGHCに取り組んだグラスゴーの人々によって発明されたグラスゴーのイディオムと呼ぶことにします。


4
「末尾再帰ワーカーループの総称」の+1。これは、私が見たほとんどの用途に一般的に当てはまるようです。関数の場合f、私は通常f'、この種のものの名前として使用goしますが、一種のキーワードに近いイディオムとして使用することは、私が理解しようとするものです。showInt同じガードを複数回評価することを避けるためにイディオムを使用していることに注意してください。
ダンバートン

1
ところで、「名前は正確には何を意味するのでしょうか?」私はそれがgoto、そしてヘルパー関数に制御を引き渡すことを示唆していると言うでしょう。
ドンスチュワート

25
私の漠然とした記憶は、これがサイモンPJ主義であるということです。loopすでにgo規則を使用しているコードを変更しない限り、私は使用する傾向があります。「ループを回る」のように、文字通り「行く」という意味だといつも思っていました。
サイモンマーロウ

5
私はいつも「行く」を、その汚い再帰的労働を始めるための労働者機能への命令として考えていました。いずれにせよ、個人的には、ストリームフュージョンスライドの1つからそれを拾いました。関数名にティックを追加すると、ティックを忘れてしまうという問題が常にあったからです。
ハインリッヒアプフェルムス

4
Haskellよりも前の起源があると思います。Goは(スキームにすることができますという名前に人気の名前ですgoogle.com/...google.com/search?q=scheme+%22let+go%22+-let's+car+cdr
AtnNn

17

明らかにドンの答えは正しいものです。少し詳しく説明します(あなたが直接参照しているのは私の文章のようですので):goは2文字しかないのでいいです。

ああ、そして、Yesodの本が列挙子パッケージに多くのコンテンツを捧げている理由は、私がすでに列挙子の3部構成のチュートリアルをブログ投稿シリーズとして書いているので、それを本に含めるほうがよいと決めたからです。列挙子パッケージは、Yesod全体のさまざまな場所で使用されているため、関連性があります。


6
+1「go」が2文字しかない(そしてそれでも意味がある)というのは、過小評価されがちな事実です。Yesodの本での「go」の使用についてコメントしている間(これは、これらの例の優れた名前の選択でした、imho)、質問をする必要があると感じたときに実際に「go」を使用したStackOverflowの回答を読んでいまし。でも、イェソドの本の例は印象的だったので、すぐに思い出しました。良いもの!
ダンバートン

11

このイディオムは、線形構造(したがって「ループ」)だけでなく、分岐(ツリーのような)構造にも適用できると思います。

goパターンが累積パラメーターに対応する頻度、より一般的には、Mitch Wandが論文「継続ベースのプログラム変換戦略」(私のお気に入りの論文の1つ)で調査した継続エンコード戦略に対応する頻度はどれくらいか疑問に思います。このような場合、go関数には特定の意味があり、エレガントな仕様から効率的なコードを導出するために使用できます。


これらの関数は通常、囲んでいるスコープ内の変数を参照しているため、実際には完全ではないという単純な理由から、これらの関数の適切な名前を思い付くのは難しい場合が多いことは言うまでもありません。わかりやすい名前はばかげているように見えるでしょう:add_xまたはのようなものconsOnto_xs
dfeuer 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.