Haskell: ¿por qué la convención de nombrar una función auxiliar "ir"?

83

Veo gomucho cuando leo material o fuente de Haskell, pero nunca me he sentido realmente cómodo con eso (supongo que tiene la connotación negativa de "goto" en mi mente). Empecé a aprender Haskell con LYAH, y ahí fue donde adquirí la tendencia al uso accy stepal escribir pliegues. ¿De dónde goviene la convención para la escritura ?

Más importante aún, ¿qué se gosupone que implica exactamente el nombre ?

Dan Burton
fuente
4
En su lugar, suelo llamar a mi función loop.
2011
2
Nunca visto goen ningún material de Haskell que leí. ¿Puede dar un ejemplo / referencia?
Ionuț G. Stan
@ Ionuț Por ejemplo, la explicación del libro de Yesod del paquete Enumerator . (No sé por qué el libro de Yesod dedica tanto material al tema, pero eso no viene al caso)
Dan Burton
Por si sirve de algo, he visto a muchos programadores de C / C ++ que también nombran sus funciones auxiliares "ir" cuando no pueden pensar en un nombre mejor.
ShreevatsaR
FWIW, la recursividad de cola explícita es la versión funcional de goto de muchas maneras, incluido el potencial de ofuscación. Sin embargo, las reglas de tipificación estática y alcance ayudan a mantener la confusión al mínimo. Y en cuanto a la elección del nombre, me gusta la respuesta de @Michael Snoyman a continuación sobre la longitud y la idoneidad. Además, cuando solo hay una función de ayuda, su nombre parece en gran medida irrelevante, por lo que generalmente solo elijo 'ir' o 'bucle' porque tengo que elegir algo y ambos tienen sentido. Tiendo a preferir "ir" para bucles perezosos y "bucle" para los estrictos.
mokus

Respuestas:

137

¡Hmm! ¡Un poco de arqueología!

Desde alrededor de 2004 lo he usado gocomo nombre genérico para los bucles de trabajo recursivos de cola, cuando hago una transformación de trabajador / contenedor de una función recursiva. Empecé a usarlo ampliamente en bytestring, por ejemplo

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

era de bytestringagosto de 2005.

Esto se escribió en RWH y probablemente se popularizó a partir de ahí. Además, en la biblioteca de stream fusion , Duncan Coutts y yo comenzamos a hacerlo mucho.

De las fuentes de GHC

Sin embargo, el idioma se remonta más atrás. foldren GHC.Base se da como:

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

que es probablemente donde aprendí el truco (pensé que esto era de la tesis de Andy Gill, pero no puedo encontrar ningún uso goallí). No se da de esta forma en Gofer, así que creo que apareció por primera vez en la base de código de GHC.

Para 2001, Simon Marlow estaba usando goalgunos de los códigos a nivel de sistemas, por lo que podríamos echar la culpa en algún lugar de GHC, y esta pista nos lleva a la fuente de GHC , donde gose usa ampliamente en funciones de trabajador:

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

Al desenterrar versiones antiguas de GHC, vemos que en GHC 0.29 este idioma no aparece, pero en la serie GHC 3.02 (1998), el goidioma aparece en todas partes. Un ejemplo, en Numeric.lhsla definición de showInt, con fecha de 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'
      }}

esta es una implementación diferente a la dada en el informe H98 . Sin embargo, al profundizar en la implementación de "Numeric.lhs" , encontramos que no es la misma que la versión que se agregó a GHC 2.06 en 1997, y aparece un parche muy interesante de Sigbjorne Finne, en abril de 1998, que agrega un gobucle a Numeric.lhs.

Esto dice que al menos en 1998, Sigbjorne estaba agregando gobucles a la biblioteca "std" de GHC, mientras que simultáneamente, muchos módulos en el núcleo del compilador de GHC tenían gobucles. Profundizando más, esta confirmación muy interesante de Will Partain en julio de 1996 agrega un bucle "go" en GHC , ¡aunque el código proviene de Simon PJ!

Así que voy a llamar a esto como un idioma de Glasgow inventado por personas en Glasgow que trabajaron en GHC a mediados de los 90, como Simon Marlow , Sigbjorn Finne , Will Partain y Simon Peyton Jones .

Don Stewart
fuente
4
+1 para "el nombre genérico de los bucles de trabajo recursivos de cola", que parece ser generalmente cierto para la mayoría de los usos que he visto. Para una función f, personalmente la usaré f'como el nombre de este tipo de cosas, aunque usarla gocomo una especie de modismo cercano a una palabra clave es algo que podría intentar captar. Es interesante notar que showIntusa el modismo para evitar evaluar al mismo guardia varias veces.
Dan Burton
1
Por cierto, para "¿qué se supone que implica exactamente el nombre go?" Yo diría que insinúa gotoy entrega el control a una función de ayuda.
Don Stewart
25
Mi vago recuerdo es que esto es un PJ-ismo de Simon. Tiendo a usarlo a loopmenos que esté modificando un código que ya usa la goconvención. Siempre pensé que tenía la intención de significar literalmente "ir", como en "dar la vuelta al circuito".
Simon Marlow
5
Siempre pensé en "ir" como un comando a la función del trabajador para comenzar su trabajo recursivo sucio. En cualquier caso, personalmente, lo recogí de una de las diapositivas de fusión de flujo porque agregar ticks al nombre de la función siempre tenía el problema de que me olvidaba del tick.
Heinrich Apfelmus
4
Creo que tiene orígenes anteriores a Haskell. go es un nombre popular para el esquema de
permisos
17

Obviamente, la respuesta de Don es la correcta. Permítanme agregar un pequeño detalle (ya que parece ser mi escritura a la que se refiere directamente): ir es bueno porque solo tiene dos letras.

Ah, y la razón por la que el libro de Yesod dedica tanto contenido al paquete del enumerador es porque ya había escrito el tutorial de tres partes del enumerador como una serie de publicaciones de blog, así que decidí que también podría incluirlo en el libro. El paquete de enumeradores se utiliza en varios lugares de Yesod, por lo que es relevante.

Michael Snoyman
fuente
6
+1 "go" siendo solo 2 letras (y aún significativo) es un hecho que es fácil de subestimar. Mientras comenté sobre el uso de "ir" en el libro de Yesod (que fue una excelente elección de nombre para esos ejemplos, en mi humilde opinión), en realidad estaba leyendo una respuesta de StackOverflow que usaba "ir" cuando sentí que debería hacer la pregunta. Sin embargo, inmediatamente recordé el ejemplo del libro de Yesod porque fue memorable. ¡Buen material!
Dan Burton
11

Espero que este idioma sea aplicable no solo a estructuras lineales (y por lo tanto "bucles"), sino también a estructuras ramificadas (en forma de árbol).

Me pregunto con qué frecuencia el gopatrón se corresponde con los parámetros de acumulación y, en general, con las estrategias de codificación de continuación que Mitch Wand exploró en el artículo Estrategias de transformación de programas basados en la continuación (uno de mis artículos favoritos de todos los tiempos). En estos casos, la gofunción tiene un significado particular, que luego puede usarse para derivar un código eficiente a partir de una especificación elegante.

Conal
fuente
Creo que vale la pena mencionar que a menudo es difícil encontrar buenos nombres para estas funciones por la sencilla razón de que normalmente se refieren a variables en un ámbito adjunto y, por lo tanto, no están realmente completas. Un nombre descriptivo probablemente se vería tonto: algo como add_xo consOnto_xs.
dfeuer