Haskell: tại sao quy ước đặt tên một hàm trợ giúp là “go”?


83

Tôi thấy gorất nhiều khi đọc tài liệu hoặc nguồn của Haskell, nhưng tôi chưa bao giờ thực sự thoải mái về nó - (tôi đoán nó mang hàm ý tiêu cực của "goto" trong tâm trí tôi). Tôi bắt đầu học Haskell với LYAH, và đó là nơi tôi chọn ra xu hướng sử dụng accstepkhi viết các nếp gấp. Quy ước về chữ viết gobắt nguồn từ đâu?

Quan trọng nhất, chính xác cái tên gođược cho là ngụ ý gì?


4
Tôi thường gọi hàm của mình loopđể thay thế.
augustss

2
Chưa từng thấy gotrong bất kỳ tài liệu Haskell nào mà tôi đã đọc. Bạn có thể đưa ra một ví dụ / tài liệu tham khảo?
Ionuț G. Stan

@ Ionuț Ví dụ, phần giải thích của sách Yesod về gói Điều tra viên . (Tại sao dành cuốn sách Yesod quá nhiều vật chất cho chủ đề này tôi không biết, nhưng đó của bên cạnh điểm)
Dan Burton

Đối với những gì nó đáng giá, tôi đã thấy nhiều lập trình viên C / C ++ cũng đặt tên cho các hàm trợ giúp của họ là "đi" khi họ không thể nghĩ ra một cái tên tốt hơn.
ShreevatsaR

FWIW, đệ quy đuôi rõ ràng phiên bản chức năng của goto theo nhiều cách, bao gồm cả khả năng gây xáo trộn. Tuy nhiên, các quy tắc gõ tĩnh và xác định phạm vi giúp giảm thiểu sự nhầm lẫn. Và đối với lựa chọn tên, tôi thích câu trả lời của @Michael Snoyman dưới đây về độ dài và sự phù hợp. Ngoài ra, khi chỉ có một chức năng trợ giúp, tên của nó dường như phần lớn không liên quan, vì vậy tôi thường chỉ chọn 'go' hoặc 'loop' vì tôi phải chọn một cái gì đó và cả hai đều có ý nghĩa. Tôi có xu hướng thích 'go' cho các vòng lặp lười biếng và "vòng lặp" cho các vòng lặp nghiêm ngặt.
mokus

Câu trả lời:


137

Hừ! Khảo cổ học nào đó!

Kể từ khoảng năm 2004, tôi đã sử dụng golàm tên chung cho các vòng lặp worker đệ quy đuôi, khi thực hiện chuyển đổi worker / wrapper của một hàm đệ quy. Tôi bắt đầu sử dụng nó rộng rãi trong bytestring, ví dụ:

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

là từ bytestringtháng 8 năm 2005.

Điều này đã được viết trong RWH , và có lẽ đã được phổ biến từ đó. Ngoài ra, trong thư viện tổng hợp luồng , Duncan Coutts và tôi đã bắt đầu làm việc đó rất nhiều.

Từ các nguồn GHC

Mặc dù vậy, thành ngữ còn đi xa hơn. foldrtrong GHC.Base được đưa ra là:

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

đó có lẽ là nơi tôi đã chọn ra mẹo (tôi đã nghĩ rằng đây là từ luận án của Andy Gill, nhưng không thể tìm thấy bất kỳ công dụng nào goở đó). Nó không được đưa ra dưới dạng này trong Gofer, vì vậy tôi nghĩ nó xuất hiện lần đầu tiên trong cơ sở mã GHC.

Đến năm 2001, Simon Marlow đã sử dụng gotrong một số mã cấp hệ thống, vì vậy chúng ta có thể đổ lỗi ở đâu đó trong GHC và manh mối này dẫn chúng ta đến nguồn GHC , nơi gođược sử dụng rộng rãi trong các hàm worker:

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 và Glasgow

Đào lại các bản GHC cũ, chúng ta thấy trong GHC 0.29 không thấy xuất hiện thành ngữ này, nhưng đến loạt bài GHC 3.02 (1998) thì gothành ngữ này xuất hiện khắp nơi. Một ví dụ, trong Numeric.lhsđịnh nghĩa của showInt, từ năm 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'
      }}

đây là cách triển khai khác với cách được đưa ra trong báo cáo H98 . Tuy nhiên, đi sâu vào việc triển khai "Numeric.lhs" , chúng tôi thấy rằng nó không giống với phiên bản đã được thêm vào GHC 2.06 vào năm 1997 và một bản vá rất thú vị từ Sigbjorne Finne xuất hiện, vào tháng 4 năm 1998, thêm một govòng lặp tới Numeric.lhs.

Điều này nói rằng ít nhất vào năm 1998, Sigbjorne đã thêm gocác vòng lặp vào thư viện "std" GHC, trong khi đồng thời, nhiều mô-đun trong lõi trình biên dịch GHCgocác vòng lặp. Tìm hiểu sâu hơn, cam kết rất thú vị này từ Will Partain vào tháng 7 năm 1996 thêm một vòng lặp "go" vào GHC - tuy nhiên, mã đến từ Simon PJ!

Vì vậy, tôi sẽ gọi đây là một thành ngữ Glasgow được phát minh bởi những người tại Glasgow, những người làm việc trên GHC vào giữa những năm 90, chẳng hạn như Simon Marlow , Sigbjorn Finne , Will PartainSimon Peyton Jones .


4
+1 cho "tên chung cho các vòng lặp công nhân đệ quy đuôi", điều này thường đúng với hầu hết các mục đích sử dụng mà tôi đã thấy. Đối với một hàm f, cá nhân tôi thường sẽ sử dụng f'làm tên cho loại sự vật này, mặc dù việc sử dụng gonhư một loại thành ngữ gần từ khóa là điều tôi có thể cố gắng chọn. Điều thú vị cần lưu ý là showIntsử dụng thành ngữ để tránh đánh giá nhiều lần cùng một người bảo vệ.
Dan Burton

1
BTW, cho "chính xác cái tên được cho là ngụ ý gì?" Tôi sẽ nói rằng nó gợi ý gotovà chuyển giao quyền kiểm soát cho một chức năng trợ giúp.
Don Stewart

25
Hồi ức mơ hồ của tôi là đây là một Simon PJ-ism. Tôi có xu hướng sử dụng looptrừ khi tôi đang sửa đổi mã đã sử dụng goquy ước. Tôi luôn nghĩ nó có nghĩa đen là "đi", như trong "đi vòng quanh".
Simon Marlow

5
Tôi luôn nghĩ về "go" như một lệnh cho hàm worker để bắt đầu quá trình lao động đệ quy bẩn thỉu của nó. Trong mọi trường hợp, theo cá nhân tôi, tôi đã chọn nó từ một trong những slide tổng hợp luồng vì việc thêm dấu tích vào tên hàm luôn có vấn đề là tôi sẽ quên dấu tích.
Heinrich Apfelmus

4
Tôi tin rằng nó có nguồn gốc từ trước Haskell. go là tên phổ biến của lược đồ cho phép được đặt tên ( google.com/… , google.com/search?q=scheme+%22let+go%22+-let's+car+cdr )
AtnNn

17

Rõ ràng câu trả lời của Don là đúng. Hãy để tôi chỉ thêm vào một chi tiết nhỏ (vì nó có vẻ là bài viết của tôi mà bạn đang trực tiếp đề cập đến): go là tốt vì nó chỉ có hai chữ cái.

Ồ, và lý do cuốn sách Yesod dành nhiều nội dung cho gói điều tra viên là vì tôi đã viết hướng dẫn ba phần về điều tra viên dưới dạng một loạt bài đăng trên blog, vì vậy tôi quyết định đưa nó vào cuốn sách. Gói điều tra viên được sử dụng ở một số nơi trên Yesod, vì vậy nó có liên quan.


6
+1 "go" chỉ là 2 chữ cái (và vẫn có nghĩa) là một thực tế dễ bị đánh giá thấp. Trong khi tôi nhận xét về cách sử dụng "go" của sách Yesod (đó là một lựa chọn tên tuyệt vời cho những ví dụ đó, imho), tôi thực sự đang đọc một câu trả lời StackOverflow sử dụng "go" khi tôi cảm thấy mình nên đặt câu hỏi. Tuy nhiên, tôi nhớ ngay đến ví dụ về cuốn sách Yesod vì nó rất đáng nhớ. Đồ tốt!
Dan Burton

11

Tôi mong rằng thành ngữ này không chỉ có thể áp dụng cho các cấu trúc tuyến tính (và do đó là "vòng lặp"), mà còn cho các cấu trúc phân nhánh (giống cây).

Tôi tự hỏi tần suất gomẫu tương ứng với các tham số tích lũy và nói chung là với các chiến lược mã hóa tiếp tục mà Mitch Wand đã khám phá trong bài báo Chiến lược chuyển đổi chương trình dựa trên liên tục (một trong những bài báo yêu thích nhất mọi thời đại của tôi). Trong những trường hợp này, gohàm có một ý nghĩa cụ thể, sau đó có thể được sử dụng để lấy mã hiệu quả từ một đặc tả trang nhã.


Tôi nghĩ rằng cần nhắc đến việc thường khó tìm ra những cái tên hay cho những hàm này vì lý do đơn giản là chúng thường tham chiếu đến các biến trong một phạm vi bao quanh và do đó không thực sự hoàn chỉnh. Một tên mô tả có thể trông ngớ ngẩn: đại loại như add_xhoặc consOnto_xs.
dfeuer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.