Haskell: dlaczego konwencja nazywania funkcji pomocniczej „go”?

84

goDużo widzę czytając materiał lub źródło Haskella, ale nigdy nie czułem się z tym komfortowo - (myślę, że ma to negatywne konotację „goto”). Zacząłem uczyć się języka Haskell z LYAH i wtedy wychwyciłem tendencję do używania acci steppisania fałd. Skąd się wzięła konwencja pisania go?

A co najważniejsze, co dokładnie goma oznaczać ta nazwa ?

Dan Burton
źródło
4
Zwykle loopzamiast tego wzywam moją funkcję .
sierpień
2
Nigdy nie widziałem gow żadnym materiale Haskella, który czytałem. Czy możesz podać przykład / odniesienie?
Ionuț G. Stan
@ Ionuț Na przykład wyjaśnienie w książce Yesod dotyczące pakietu Enumerator . (Dlaczego książka Yesod poświęca tak dużo materiału na ten temat, którego nie wiem, ale to nie ma znaczenia)
Dan Burton
Co więcej, widziałem wielu programistów C / C ++, którzy również nazywali swoje funkcje pomocnicze „idą”, kiedy nie mogli wymyślić lepszej nazwy.
ShreevatsaR
FWIW, jawna rekurencja ogona jest funkcjonalną wersją goto na wiele sposobów, w tym z możliwością zaciemniania. Jednak statyczne reguły typowania i zakresu pomagają ograniczyć zamieszanie do minimum. A jeśli chodzi o wybór nazwy, podoba mi się poniższa odpowiedź @Michaela Snoymana na temat długości i przydatności. Ponadto, gdy jest tylko jedna funkcja pomocnicza, jej nazwa wydaje się w dużej mierze nieistotna, więc generalnie wybieram „go” lub „pętla”, ponieważ muszę coś wybrać i obie mają sens. Wolę „idź” dla leniwych pętli i „pętla” dla ścisłych.
mokus

Odpowiedzi:

138

Hmm! Trochę archeologii!

Od około 2004 r. Używam gonazwy ogólnej dla ogonowych rekurencyjnych pętli roboczych podczas wykonywania transformacji pracownika / opakowania funkcji rekurencyjnej. Zacząłem go szeroko stosować bytestringnp

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

był z bytestringsierpnia 2005 roku.

Zostało to zapisane w RWH i prawdopodobnie stamtąd zostało spopularyzowane. Ponadto Duncan Coutts i ja zaczęliśmy to często robić w bibliotece stream fusion .

Ze źródeł GHC

Idiom sięga jednak dalej. foldrw GHC.Base jest podana jako:

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

na którym prawdopodobnie podjąłem sztuczkę (myślałem, że to z pracy Andy'ego Gilla, ale nie mogę tam znaleźć żadnego zastosowania go). Nie jest podawany w tej formie w Gofer, więc myślę, że po raz pierwszy pojawił się w bazie kodu GHC.

Do 2001 roku Simon Marlow używał gow niektórych kodach na poziomie systemu, więc możemy przypisać winę gdzieś w GHC, a ta wskazówka prowadzi nas do źródła GHC , gdzie gojest szeroko stosowany w funkcjach roboczych:

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

Odkopując stare wersje GHC, widzimy, że w GHC 0.29 ten idiom się nie pojawia, ale w serii GHC 3.02 (1998) goidiom pojawia się wszędzie. Przykład Numeric.lhsw definicji showIntz lat 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'
      }}

jest to inna implementacja niż ta przedstawiona w raporcie H98 . Zagłębiając się w implementację „Numeric.lhs” , okazuje się jednak, że nie jest to to samo, co wersja dodana do GHC 2.06 w 1997 r., A bardzo interesująca łatka od Sigbjorne Finne pojawia się w kwietniu 1998 r., Dodając gopętla do Numeric.lhs.

Oznacza to, że przynajmniej do 1998 roku Sigbjorne dodawał gopętle do biblioteki „std” GHC, podczas gdy jednocześnie wiele modułów w rdzeniu kompilatora GHC miało gopętle. Idąc dalej, to bardzo interesujące wydanie Willa Partaina z lipca 1996 roku dodaje pętlę „go” do GHC - kod pochodzi jednak od Simona PJ!

Nazywam to więc idiomem Glasgow wymyślonym przez ludzi z Glasgow, którzy pracowali nad GHC w połowie lat 90., takich jak Simon Marlow , Sigbjorn Finne , Will Partain i Simon Peyton Jones .

Don Stewart
źródło
4
+1 dla „ogólnej nazwy rekurencyjnych pętli roboczych typu tail”, która wydaje się być generalnie prawdziwa dla większości zastosowań, które widziałem. W przypadku funkcji fosobiście zwykle używam f'jako nazwy dla tego rodzaju rzeczy, chociaż używanie gojako czegoś w rodzaju idiomu zbliżonego do słowa kluczowego jest czymś, co mógłbym spróbować złapać. Ciekawe, że showIntużywa idiomu, aby uniknąć wielokrotnej oceny tego samego strażnika.
Dan Burton,
1
Swoją drogą, dla "co dokładnie ma sugerować nazwa?" Powiedziałbym, że to aluzja do gotoprzekazania kontroli funkcji pomocniczej.
Don Stewart
26
Moje mgliste wspomnienie jest takie, że jest to PJ-izm Simona. Zwykle używam, loopchyba że modyfikuję kod, który już korzysta z gokonwencji. Zawsze myślałem, że to znaczy dosłownie „idź”, na przykład „obejdź pętlę”.
Simon Marlow
6
Zawsze myślałem o „go” jako poleceniu dla funkcji robotniczej, aby rozpocząć jej brudną, rekurencyjną pracę. W każdym razie osobiście wziąłem go z jednego ze slajdów stream fusion, ponieważ dodanie znaczników do nazwy funkcji zawsze powodowało problem, że zapomniałem tika.
Heinrich Apfelmus
4
Wydaje mi się, że ma to swoje korzenie przed Haskellem. go to popularna nazwa nazwanych let in scheme ( google.com/… , google.com/search?q=scheme+%22let+go%22+-let's+car+cdr )
AtnNn
17

Oczywiście odpowiedź Dona jest prawidłowa. Pozwólcie, że dodam tylko mały szczegół (ponieważ wydaje się, że jest to moje pismo, do którego bezpośrednio się odnosicie): go jest fajne, ponieważ składa się tylko z dwóch liter.

Aha, a powodem, dla którego książka Yesod poświęca tak dużo treści pakietowi modułu wyliczającego, jest to, że napisałem już trzyczęściowy samouczek enumeratora jako serię postów na blogu, więc zdecydowałem, że równie dobrze mogę go włączyć do książki. Pakiet modułu wyliczającego jest używany w wielu miejscach w Yesod, więc jest istotny.

Michael Snoyman
źródło
6
+1 „idź” to tylko 2 litery (i nadal znaczące) to fakt, który łatwo nie docenić. Podczas gdy komentowałem użycie słowa „go” w książce Yesod (co było doskonałą nazwą dla tych przykładów, imho), tak naprawdę czytałem odpowiedź StackOverflow, w której użyto „go”, kiedy czułem, że powinienem zadać pytanie. Jednak od razu przypomniałem sobie przykład książki Yesod, ponieważ był niezapomniany. Dobry towar!
Dan Burton
11

Spodziewałbym się, że ten idiom będzie miał zastosowanie nie tylko do struktur liniowych (a więc „pętli”), ale także do struktur rozgałęzionych (drzewiastych).

Zastanawiam się, jak często gowzór odpowiada parametrom akumulacji i, bardziej ogólnie, strategiom kodowania kontynuacji, które Mitch Wand zbadał w artykule Continuation-Based Program Transformation Strategies (jednym z moich ulubionych artykułów wszechczasów). W takich przypadkach gofunkcja ma szczególne znaczenie, które można następnie wykorzystać do wyprowadzenia wydajnego kodu z eleganckiej specyfikacji.

Conal
źródło
Myślę, że warto wspomnieć, że często trudno jest wymyślić dobre nazwy dla tych funkcji z tego prostego powodu, że zazwyczaj odnoszą się one do zmiennych w zakresie obejmującym i dlatego nie są tak naprawdę kompletne. Opisowa nazwa prawdopodobnie wyglądałaby głupio: coś w rodzaju add_xlub consOnto_xs.
dfeuer