Haskell: Warum die Konvention, eine Hilfsfunktion "go" zu nennen?

83

Ich sehe goviel, wenn ich Haskell-Material oder -Quelle lese, aber ich habe mich nie wirklich wohl gefühlt - (ich denke, es hat die negative Konnotation von "goto" in meinem Kopf). Ich habe angefangen, Haskell mit LYAH zu lernen, und dort habe ich die Tendenz zum Verwenden accund stepbeim Schreiben von Falten aufgegriffen . Woher kommt die Konvention zum Schreiben go?

Was genau soll der Name gobedeuten?

Dan Burton
quelle
4
Normalerweise rufe ich loopstattdessen meine Funktion auf .
August
2
Nie goin einem Haskell-Material gesehen, das ich gelesen habe. Können Sie ein Beispiel / eine Referenz geben?
Ionuț G. Stan
@ Ionuț Zum Beispiel die Erklärung des Yesod-Buches zum Enumerator-Paket . (Warum das Jessod-Buch so viel Material dem Thema widmet, weiß ich nicht, aber das ist nebensächlich)
Dan Burton
Für das, was es wert ist, habe ich viele C / C ++ - Programmierer gesehen, die ihre Hilfsfunktionen auch "go" nennen, wenn sie sich keinen besseren Namen vorstellen können.
ShreevatsaR
FWIW, explizite Schwanzrekursion ist in vielerlei Hinsicht die funktionale Version von goto, einschließlich des Potentials zur Verschleierung. Statische Typisierungs- und Gültigkeitsregeln helfen jedoch dabei, die Verwirrung auf ein Minimum zu beschränken. Und was die Wahl des Namens angeht, ich mag die Antwort von @Michael Snoyman unten über Länge und Eignung. Wenn es nur eine Hilfsfunktion gibt, scheint der Name weitgehend irrelevant zu sein, daher wähle ich im Allgemeinen entweder "go" oder "loop", weil ich etwas auswählen muss und beide sinnvoll sind. Ich bevorzuge "go" für Lazy Loops und "Loop" für strenge.
Mokus

Antworten:

137

Hmm! Etwas Archäologie!

Seit ungefähr 2004 verwende ich goals generischen Namen für schwanzrekursive Worker-Schleifen, wenn ich eine Worker / Wrapper-Transformation einer rekursiven Funktion durchführe. Ich habe angefangen, es weit verbreitet zu verwenden bytestring, z

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

war von bytestringim August 2005.

Dies wurde in RWH geschrieben und wahrscheinlich von dort populär gemacht. Außerdem haben Duncan Coutts und ich in der Stream-Fusion- Bibliothek viel damit begonnen.

Aus den GHC-Quellen

Die Redewendung geht jedoch weiter zurück. foldrin GHC.Base wird angegeben als:

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

Hier habe ich wahrscheinlich den Trick aufgegriffen (ich hatte gedacht, dass dies aus Andy Gills These stammt, kann dort aber keine Verwendung gofinden). Ist es nicht in dieser Form in Gofer angegeben, daher denke ich, dass dies zuerst in der GHC-Codebasis erschien.

Bis 2001 verwendete Simon Marlow einen goTeil des Codes auf Systemebene, sodass wir die Schuld möglicherweise irgendwo in GHC platzieren. Dieser Hinweis führt uns zur GHC-Quelle , godie in Worker-Funktionen weit verbreitet ist:

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 und Glasgow

Wenn wir alte Versionen von GHC ausgraben, sehen wir, dass in GHC 0.29 dieses Idiom nicht vorkommt, aber in der GHC 3.02-Serie (1998) erscheint das goIdiom überall. Ein Beispiel Numeric.lhsin der Definition von showInt1996-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'
      }}

Dies ist eine andere Implementierung als die eine als im H98-Bericht angegeben . Wenn wir uns jedoch mit der Implementierung von "Numeric.lhs" befassen , stellen wir fest, dass es nicht mit der Version übereinstimmt, die 1997 zu GHC 2.06 hinzugefügt wurde, und im April 1998 erscheint ein sehr interessanter Patch von Sigbjorne Finne, der a hinzufügt goSchleife zu Numeric.lhs.

Dies besagt, dass Sigbjorne zumindest 1998 goSchleifen zur GHC "std" -Bibliothek hinzufügte , während gleichzeitig viele Module in dem GHC Compiler Kern hatten goSchleifen. Dieses sehr interessante Commit von Will Partain im Juli 1996 fügt GHC eine "Go" -Schleife hinzu - der Code stammt jedoch von Simon PJ!

Ich werde dies als eine Glasgow-Sprache bezeichnen , die von Leuten in Glasgow erfunden wurde, die Mitte der 90er Jahre an GHC gearbeitet haben, wie Simon Marlow , Sigbjorn Finne , Will Partain und Simon Peyton Jones .

Don Stewart
quelle
4
+1 für "den generischen Namen für schwanzrekursive Worker-Schleifen", was für die meisten Anwendungen, die ich gesehen habe, allgemein zu gelten scheint. Für eine Funktion werde fich normalerweise persönlich f'als Namen für diese Art von Dingen verwenden, obwohl goich versuchen könnte , sie als eine Art Keyword-Idiom zu verwenden. Interessant zu bemerken, dass showIntdie Redewendung verwendet wird, um zu vermeiden, dass dieselbe Wache mehrmals bewertet wird.
Dan Burton
1
Übrigens, für "was genau soll der Name gehen?" Ich würde sagen, es deutet darauf hin goto, dass die Kontrolle an eine Hilfsfunktion übergeben wird.
Don Stewart
25
Meine vage Erinnerung ist, dass dies ein Simon PJ-Ismus ist. Ich neige dazu, zu verwenden, es loopsei denn, ich ändere Code, der bereits die goKonvention verwendet. Ich dachte immer, es sollte wörtlich "gehen" bedeuten, wie in "um die Schleife gehen".
Simon Marlow
5
Ich dachte immer an "go" als Befehl an die Arbeiterfunktion, ihre schmutzige rekursive Arbeit zu beginnen. Auf jeden Fall habe ich es persönlich von einer der Stream-Fusion-Folien aufgenommen, da das Hinzufügen von Ticks zum Funktionsnamen immer das Problem hatte, dass ich das Tick vergessen würde.
Heinrich Apfelmus
4
Ich glaube, es hat Ursprünge vor Haskell. go ist ein beliebter Name für benannte
Let-
17

Offensichtlich ist Dons Antwort die richtige. Lassen Sie mich nur ein kleines Detail hinzufügen (da es mein Schreiben zu sein scheint, auf das Sie sich direkt beziehen): go ist schön, weil es nur zwei Buchstaben sind.

Oh, und der Grund, warum das Yesod-Buch dem Enumerator-Paket so viel Inhalt widmet, ist, dass ich das dreiteilige Tutorial von Enumerator bereits als Blog-Post-Serie geschrieben habe und mich daher entschlossen habe, es auch in das Buch aufzunehmen. Das Enumerator-Paket wird an mehreren Stellen in Yesod verwendet, daher ist es relevant.

Michael Snoyman
quelle
6
+1 "go", das nur aus 2 Buchstaben besteht (und immer noch aussagekräftig ist), ist eine Tatsache, die leicht zu unterschätzen ist. Während ich mich über die Verwendung von "go" im Yesod-Buch äußerte (was für diese Beispiele eine ausgezeichnete Namenswahl war, imho), las ich tatsächlich eine StackOverflow-Antwort , die "go" verwendete, als ich der Meinung war, ich sollte die Frage stellen. Ich erinnerte mich jedoch sofort an das Beispiel des Jessod-Buches, weil es unvergesslich war. Gutes Zeug!
Dan Burton
11

Ich würde erwarten, dass diese Redewendung nicht nur auf lineare Strukturen (und damit auf "Schleifen") anwendbar ist, sondern auch auf verzweigte (baumartige) Strukturen.

Ich frage mich, wie oft das goMuster Akkumulationsparametern und allgemeiner den Strategien zur Fortsetzung der Codierung entspricht, die Mitch Wand in der Arbeit Continuation-Based Program Transformation Strategies (eine meiner Lieblingsarbeiten aller Zeiten) untersucht hat. In diesen Fällen hat die goFunktion eine bestimmte Bedeutung, die dann verwendet werden kann, um effizienten Code aus einer eleganten Spezifikation abzuleiten.

Conal
quelle
Ich denke, es ist zu erwähnen, dass es oft schwierig ist, gute Namen für diese Funktionen zu finden, weil sie sich normalerweise auf Variablen in einem umschließenden Bereich beziehen und daher nicht wirklich vollständig sind. Ein beschreibender Name würde wahrscheinlich albern aussehen: so etwas wie add_xoder consOnto_xs.
Feuer