Хаскелл: чому конвенція називати допоміжну функцію "go"?


83

Я goбагато бачу, читаючи матеріал або джерело Haskell, але мені ніколи це не сподобалось - (мабуть, у мене на думці негативний відтінок "goto"). Я почав вивчати Haskell з LYAH, і саме тут я взяв тенденцію до використання accта stepпід час написання складок. Звідки береться конвенція про письмо go?

Найголовніше, що саме goмає означати назва ?


4
loopЗамість цього я зазвичай викликаю свою функцію .
серпень

2
Ніколи не бачив goу жодному прочитаному мною матеріалі Haskell. Чи можете ви навести приклад / посилання?
Ionuț G. Stan

@ Ionuț Наприклад, пояснення книги Есода щодо пакета Enumerator . (Чому книга Єсода присвячує стільки матеріалу темі, яку я не знаю, але це не суть справи)
Ден Бертон

Наскільки це варте, я бачив багатьох програмістів на C / C ++, які також називають свої допоміжні функції "go", коли вони не можуть придумати кращої назви.
ShreevatsaR

FWIW, явна рекурсія хвоста є функціональною версією goto багатьма способами, включаючи потенціал затухання. Статичні правила набору тексту та масштабування допомагають звести плутанину до мінімуму. Що стосується вибору імені, мені подобається відповідь @Michael Snoyman нижче про довжину та придатність. Крім того, коли є лише одна допоміжна функція, її назва здається значною мірою неактуальною, тому я, як правило, просто вибираю або "йти", або "петля", тому що я повинен щось вибрати, і це обидва має сенс. Я, як правило, віддаю перевагу "йти" для ледачих петель і "петлі" для строгих.
mokus

Відповіді:


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

було з bytestringсерпня 2005 року.

Це було написано в RWH , і, мабуть, було популярно звідти. Крім того, у бібліотеці потокового злиття , ми з Duncan Coutts почали це робити багато.

З джерел GHC

Ідіома повертається ще далі. foldrв GHC. База подана як:

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

що, мабуть, я і взяв на хитрість (я думав, що це з тези Енді Гілла, але не можу знайти там ніякої користі 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)

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" , ми виявляємо, що це не те саме, що версія, додана до GHC 2.06 в 1997 році, і дуже цікавий патч від Sigbjorne Finne з'являється в квітні 1998 року, додаючи goцикл до Numeric.lhs.

Це говорить про те, що принаймні до 1998 року Sigbjorne додавав goцикли до бібліотеки "std" GHC, тоді як одночасно багато модулів в ядрі компілятора GHC мали goцикли. Копаючи далі, цей дуже цікавий коміт від Уілла Партайна в липні 1996 р. Додає цикл "йти" до GHC - проте код походить від Simon PJ!

Тож я збираюся називати це ідіомою Глазго, винайденою людьми у Глазго, які працювали над GHC у середині 90-х, такими як Саймон Марлоу , Зігбйорн Фінне , Уілл Партайн та Саймон Пейтон Джонс .


4
+1 для "загальної назви для хвостово-рекурсивних робітничих циклів", що, як видається, є справедливим для більшості застосувань, які я бачив. Як функцію fя особисто зазвичай використовую f'як назву для такого роду речей, хоча використання goяк своєрідної ідіоми ключового слова - це те, що я міг би спробувати підібрати. Цікаво відзначити, що showIntвикористовує ідіому, щоб уникнути оцінки одного і того ж охоронця кілька разів.
Ден Бертон,

1
До речі, "що саме означає назва, яку слід передбачати?" Я б сказав, що це натякає на gotoпередачу контролю допоміжній функції.
Дон Стюарт,

25
Я невиразно згадую, що це піж-сизм Саймона. Я схильний використовувати, loopякщо не модифікую код, який уже використовує goконвенцію. Я завжди думав, що це означає буквально "йти", як у "обійти петлю".
Саймон Марлоу,

5
Я завжди думав про "йти" як про команду робочій функції розпочати свою брудну рекурсивну роботу. У будь-якому випадку особисто я взяв його з одного зі слайдів потоку злиття, оскільки додавання галочок до імені функції завжди мало проблему, що я забуду галочку.
Генріх Апфельмус,

4
Я вважаю, що воно походить від Хаскелла. go - це популярна назва для іменованих
дозволених

17

Очевидно, що відповідь Дона правильна. Дозвольте мені лише додати невелику деталь (оскільки, здається, це моє написання, на яке ви безпосередньо посилаєтесь): go є приємним, тому що це лише дві букви.

О, і причина, по якій книга Єсода приділяє стільки вмісту пакунку перелічувачів, полягає в тому, що я вже написав підручник з переписувача з трьох частин як серію дописів у блозі, тому вирішив, що можу також включити його до книги. Пакет перечислювача використовується в багатьох місцях по всьому Єсоду, тому він є актуальним.


6
+1 "йти" - лише дві букви (і все ще значуще) - це факт, який легко недооцінити. Поки я коментував використання книжки Yesod "go" (що було чудовим вибором імені для цих прикладів, imho), я насправді читав відповідь StackOverflow, яка використовувала "go", коли я відчував, що повинен задати питання. Однак я одразу згадав приклад книги Єсода, бо він запам’ятався. Хороший матеріал!
Ден Бертон,

11

Я би очікував, що ця ідіома застосовуватиметься не лише до лінійних структур (а отже, і "петель"), але й до розгалужених (деревоподібних) структур.

Цікаво, як часто goшаблон відповідає параметрам накопичення та, загальніше, стратегіям кодування продовження, які Мітч Ванд досліджував у статті Програми трансформації на основі продовження програми (одна з моїх найулюбленіших статей). У цих випадках goфункція має особливе значення, яке потім може бути використано для отримання ефективного коду з елегантної специфікації.


Думаю, не можна не згадати, що часто важко придумати хороші імена для цих функцій з тієї простої причини, що вони, як правило, посилаються на змінні в області, що охоплює, і тому насправді не є повноцінними. Описова назва могла б виглядати безглуздо: щось на зразок add_xабо consOnto_xs.
dfeuer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.