Haskell, Lisp และ verbosity [ปิด]


104

สำหรับคนที่คุณมีประสบการณ์ทั้ง Haskell และ Lisp ฉันอยากรู้ว่า "น่าพอใจ" (ใช้คำที่น่ากลัว) อย่างไรการเขียนโค้ดใน Haskell vs. Lisp

ภูมิหลังบางอย่าง: ตอนนี้ฉันกำลังเรียนรู้ Haskell โดยก่อนหน้านี้เคยทำงานร่วมกับ Scheme และ CL (และโจมตี Clojure เล็กน้อย) ตามเนื้อผ้าคุณอาจคิดว่าฉันเป็นแฟนตัวยงของภาษาแบบไดนามิกเนื่องจากมีความกระชับและรวดเร็ว ฉันตกหลุมรักมาโคร Lisp อย่างรวดเร็วเพราะมันเป็นอีกวิธีหนึ่งในการหลีกเลี่ยงความฟุ่มเฟื่อยและสำเร็จรูป

ฉันพบว่า Haskell น่าสนใจอย่างไม่น่าเชื่อเนื่องจากกำลังแนะนำวิธีการเขียนโค้ดที่ฉันไม่รู้ว่ามีอยู่จริง แน่นอนว่ามีบางแง่มุมที่ดูเหมือนว่าจะช่วยในการบรรลุความคล่องตัวเช่นความสะดวกในการเขียนฟังก์ชันบางส่วน อย่างไรก็ตามฉันกังวลเล็กน้อยเกี่ยวกับการสูญเสียมาโคร Lisp (ฉันคิดว่าฉันสูญเสียมันไปความจริงจะบอกว่าฉันอาจยังไม่ได้เรียนรู้เกี่ยวกับพวกมัน?) และระบบการพิมพ์แบบคงที่

ใครก็ตามที่ได้ทำการเข้ารหัสในระดับที่เหมาะสมในทั้งสองโลกจะแสดงความคิดเห็นว่าประสบการณ์นั้นแตกต่างกันอย่างไรซึ่งคุณต้องการและหากกล่าวว่าความชอบเป็นสถานการณ์

คำตอบ:


69

คำตอบสั้น ๆ :

  • เกือบทุกอย่างที่คุณสามารถทำได้ด้วยมาโครที่คุณสามารถทำได้ด้วยฟังก์ชันลำดับที่สูงขึ้น (และฉันรวม monads ลูกศร ฯลฯ ) แต่อาจต้องใช้ความคิดมากขึ้น (แต่เป็นครั้งแรกเท่านั้นและมันก็สนุกและคุณจะเป็น โปรแกรมเมอร์ที่ดีกว่าสำหรับมัน) และ
  • ระบบคงที่เป็นระบบทั่วไปเพียงพอที่จะไม่มาขวางทางคุณและที่น่าแปลกใจก็คือ "ช่วยในการบรรลุความคล่องตัว" (ตามที่คุณกล่าว) เพราะเมื่อโปรแกรมของคุณรวบรวมคุณสามารถมั่นใจได้ว่าเกือบถูกต้องดังนั้นความมั่นใจนี้ช่วยให้คุณลอง จากสิ่งที่คุณอาจกลัวที่จะลอง - มีความรู้สึก "พลวัต" ในการเขียนโปรแกรมแม้ว่าจะไม่เหมือนกับ Lisp ก็ตาม

[หมายเหตุ: มี " เทมเพลต Haskell " ที่ให้คุณเขียนมาโครได้เช่นเดียวกับใน Lisp แต่พูดอย่างเคร่งครัดว่าคุณไม่จำเป็นต้องใช้เลย]


15
จาก Conor McBride อ้างโดย Don Stewart: 'ฉันชอบคิดว่าประเภทต่างๆกำลังแปรปรวนตามแรงโน้มถ่วงของเราเพื่อให้ทิศทางที่เราต้องเดินทาง [เพื่อเขียนโปรแกรมที่ถูกต้อง] กลายเป็น "ตกต่ำ" ระบบประเภททำให้การเขียนโปรแกรมที่ถูกต้องเป็นเรื่องง่ายอย่างน่าประหลาดใจ ... ดูโพสต์นี้และการแชร์ซ้ำ
ShreevatsaR

3
ฟังก์ชันลำดับสูงไม่สามารถแทนที่มาโครได้และในความเป็นจริง CL มีทั้งสองอย่างด้วยเหตุผลบางประการ พลังที่แท้จริงของมาโครใน CL คือช่วยให้นักพัฒนาสามารถแนะนำคุณสมบัติภาษาใหม่ ๆ ที่ช่วยในการแก้ปัญหาได้ดีขึ้นโดยไม่ต้องรอภาษาเวอร์ชันใหม่เช่นใน Haskell หรือ Java ตัวอย่างเช่นหาก Haskell มีอำนาจนี้ผู้เขียน Haskell ไม่จำเป็นต้องเขียนส่วนขยาย GHC แต่นักพัฒนาสามารถนำไปใช้เป็นมาโครได้ตลอดเวลาหรือไม่
mljrg

@mljrg คุณมีตัวอย่างที่เป็นรูปธรรมหรือไม่? ดูความคิดเห็นเกี่ยวกับคำตอบของ Hibou57 ด้านล่างซึ่งตัวอย่างที่ถูกกล่าวหากลายเป็นพิรุธ ฉันสนใจที่จะทราบประเภทของสิ่งที่คุณหมายถึง (เช่นรหัส Haskell ที่มีและไม่มีมาโคร)
ShreevatsaR

4
นำแกงจาก Haskell คุณสามารถนำไปใช้กับสิ่งที่เหลืออยู่ใน Haskell ได้หรือไม่? อีกตัวอย่างหนึ่ง: สมมติว่า Haskell ไม่รองรับการจับคู่รูปแบบคุณสามารถเพิ่มเองโดยไม่ต้องให้นักพัฒนาของ GHC สนับสนุนได้หรือไม่? ใน CL คุณสามารถใช้มาโครเพื่อขยายภาษาได้ตามต้องการ ฉันคิดว่านั่นเป็นสาเหตุที่ภาษา CL ไม่เปลี่ยนไปตั้งแต่มาตรฐานในยุค 90 ในขณะที่ Haskell ดูเหมือนจะมีส่วนขยายที่ไม่สิ้นสุดใน GHC
mljrg

64

ก่อนอื่นอย่ากังวลว่าจะสูญเสียคุณสมบัติบางอย่างเช่นการพิมพ์แบบไดนามิก ตามที่คุณคุ้นเคยกับ Common Lisp ซึ่งเป็นภาษาที่ได้รับการออกแบบมาเป็นอย่างดีฉันคิดว่าคุณคงทราบดีว่าภาษาไม่สามารถลดลงในชุดคุณลักษณะได้ ทุกอย่างเกี่ยวกับทั้งหมดที่สอดคล้องกันไม่ใช่เหรอ?

ในเรื่องนี้ Haskell เปล่งประกายเฉกเช่นเดียวกับ Common Lisp คุณสมบัติของมันรวมเข้าด้วยกันเพื่อให้คุณมีวิธีการเขียนโปรแกรมที่ทำให้โค้ดสั้นและสวยงามมาก การขาดมาโครจะบรรเทาลงได้บ้างด้วยแนวคิดที่ซับซ้อนมากขึ้น (แต่ในทำนองเดียวกันก็ยากที่จะเข้าใจและใช้) เช่น monads และลูกศร ระบบประเภทคงที่จะเพิ่มพลังของคุณแทนที่จะเข้ามาขวางคุณเหมือนในภาษาเชิงวัตถุส่วนใหญ่

ในทางกลับกันการเขียนโปรแกรมใน Haskell นั้นมีการโต้ตอบน้อยกว่า Lisp มากและการสะท้อนกลับจำนวนมหาศาลที่มีอยู่ในภาษาเช่น Lisp นั้นไม่เหมาะกับมุมมองแบบคงที่ของโลกที่ Haskell กำหนดไว้ ชุดเครื่องมือที่คุณสามารถใช้ได้จึงค่อนข้างแตกต่างกันระหว่างสองภาษา แต่ยากที่จะเปรียบเทียบกัน

โดยส่วนตัวแล้วฉันชอบวิธีการเขียนโปรแกรม Lisp โดยทั่วไปเพราะฉันรู้สึกว่ามันเหมาะกับวิธีที่ฉันทำงานได้ดีขึ้น อย่างไรก็ตามนี่ไม่ได้หมายความว่าคุณจะต้องทำเช่นนั้นเช่นกัน


4
คุณช่วยอธิบายเพิ่มเติมเกี่ยวกับ "การเขียนโปรแกรมใน Haskell มีการโต้ตอบน้อยกว่ามาก" GHCi ให้ทุกสิ่งที่คุณต้องการไม่ใช่หรือ?
Johannes Gerer

3
@JohannesGerer: ฉันยังไม่ได้ลอง แต่เท่าที่ฉันอ่าน GHCi ไม่ใช่เปลือกในภาพที่กำลังทำงานซึ่งคุณสามารถกำหนดและขยายส่วนต่างๆของโปรแกรมทั้งหมดได้ตามอำเภอใจในขณะที่กำลังทำงานอยู่ นอกจากนี้ไวยากรณ์ของ Haskell ยังทำให้การคัดลอกส่วนย่อยของโปรแกรมระหว่างการจำลองและตัวแก้ไขทางโปรแกรมทำได้ยากขึ้น
Svante

13

ความจำเป็นในการเขียนโปรแกรมใน Haskell มีน้อยกว่า Common Lisp เนื่องจากสามารถจัดโครงสร้างรอบ ๆ monads ได้มากและไวยากรณ์ที่เพิ่มเข้ามาทำให้ DSL แบบฝังดูคล้ายต้นไม้น้อยลง แต่ก็มี Template Haskell อยู่เสมอตามที่ShreevatsaRกล่าวถึงและแม้แต่Liskell (ความหมายของ Haskell + Lisp ไวยากรณ์) ถ้าคุณชอบวงเล็บ


1
Liskell ลิงค์ตาย แต่วันนี้มีHackett
Will Ness

10

เกี่ยวกับมาโครนี่คือหน้าที่พูดถึง: สวัสดี Haskell, Goodbye Lispลาเสียงกระเพื่อม อธิบายถึงมุมมองที่ไม่จำเป็นต้องใช้มาโครใน Haskell มันมาพร้อมกับตัวอย่างสั้น ๆ สำหรับการเปรียบเทียบ

ตัวอย่างกรณีที่จำเป็นต้องใช้มาโคร LISP เพื่อหลีกเลี่ยงการประเมินอาร์กิวเมนต์ทั้งสอง:

(defmacro doif (x y) `(if ,x ,y))

ตัวอย่างกรณีที่ Haskell ไม่ได้ประเมินทั้งสองอาร์กิวเมนต์อย่างเป็นระบบโดยไม่จำเป็นต้องใช้คำจำกัดความของมาโคร:

doif x y = if x then (Just y) else Nothing

และ voila


17
นั่นเป็นความเข้าใจผิดทั่วไป ใช่ในความเกียจคร้านของ Haskell หมายความว่าคุณไม่จำเป็นต้องใช้มาโครเมื่อคุณต้องการหลีกเลี่ยงการประเมินบางส่วนของนิพจน์ แต่สิ่งเหล่านี้เป็นเพียงชุดย่อยที่ไม่สำคัญที่สุดของการใช้มาโครทั้งหมด Google สำหรับ "The Swine Before Perl" สำหรับการพูดคุยที่แสดงให้เห็นถึงมาโครที่ไม่สามารถทำได้ด้วยความเกียจคร้าน นอกจากนี้ถ้าคุณไม่ต้องการบิตบางอย่างที่จะต้องเข้มงวดแล้วคุณจะไม่สามารถทำได้เช่นฟังก์ชั่น - มิเรอร์ความจริงที่ว่าโครงการของdelayไม่สามารถเป็นฟังก์ชั่น
Eli Barzilay

8
@Eli Barzilay: ฉันไม่พบว่าตัวอย่างนี้น่าเชื่อมากนัก นี่คือคำแปล Haskell ที่เรียบง่ายและสมบูรณ์ของสไลด์ 40: pastebin.com/8rFYwTrE
Reid Barton

5
@Eli Barzilay: ฉันไม่เข้าใจคำตอบของคุณเลย accept คือ (E) DSL acceptฟังก์ชั่นเป็นอะนาล็อกของแมโครที่ระบุไว้บนหน้าเว็บก่อนหน้านี้และความหมายของvเป็นตรงขนานไปกับความหมายของvในโครงการบนภาพนิ่ง 40 Haskell และโครงการฟังก์ชั่นคำนวณสิ่งเดียวกันกับกลยุทธ์การประเมินเดียวกัน อย่างดีที่สุดมาโครช่วยให้คุณสามารถเปิดเผยโครงสร้างของโปรแกรมของคุณให้กับเครื่องมือเพิ่มประสิทธิภาพได้มากขึ้น คุณแทบไม่สามารถอ้างได้ว่านี่เป็นตัวอย่างที่มาโครเพิ่มพลังในการแสดงออกของภาษาในแบบที่ไม่ได้จำลองแบบโดยการประเมินแบบขี้เกียจ
Reid Barton

5
@Eli Barzilay: ในแผนการขี้เกียจสมมุติคุณสามารถเขียนสิ่งนี้: pastebin.com/TN3F8VVE คำกล่าวอ้างทั่วไปของฉันคือมาโครนี้ซื้อคุณน้อยมาก: ไวยากรณ์ที่แตกต่างกันเล็กน้อยและเวลาที่ง่ายขึ้นสำหรับเครื่องมือเพิ่มประสิทธิภาพ (แต่มันก็ไม่สำคัญหรอก "คอมไพเลอร์ที่ชาญฉลาดเพียงพอ") ในการแลกเปลี่ยนคุณได้ขังตัวเองไว้ในภาษาที่ไม่สามารถแสดงออกได้ คุณจะกำหนดหุ่นยนต์ที่ตรงกับตัวอักษรใด ๆ โดยไม่แสดงรายการทั้งหมดได้อย่างไร นอกจากนี้ฉันไม่ทราบว่าคุณหมายถึงอะไรโดย "ใช้ในรายการย่อยทั้งหมด" หรือ "การใช้ที่จำเป็นซึ่งมีขอบเขตของตัวเอง"
Reid Barton

15
ตกลงฉันยอมแพ้ เห็นได้ชัดว่านิยามของ DSL คือ "ข้อโต้แย้งที่จะแมโคร" และอื่น ๆ ตัวอย่างเช่นขี้เกียจโครงการของฉันไม่ได้ DSL แม้จะเป็น isomorphic syntactically ไปที่เดิม ( automatonกลายเป็นletrec, :กลายเป็นaccept, ->กลายเป็นอะไรในรุ่นนี้) ก็ตาม.
Reid Barton

9

ฉันเป็นโปรแกรมเมอร์ Lisp ทั่วไป

เมื่อลองใช้ Haskell มาแล้วผลกำไรส่วนตัวของฉันคือยึดติดกับ CL

เหตุผล:

  • การพิมพ์แบบไดนามิก (ตรวจสอบการพิมพ์แบบไดนามิกเทียบกับการพิมพ์แบบคงที่ - การวิเคราะห์ตามรูปแบบโดย Pascal Costanza )
  • อาร์กิวเมนต์ที่เป็นทางเลือกและคำหลัก
  • ไวยากรณ์รายการ homoiconic เหมือนกันกับมาโคร
  • ไวยากรณ์คำนำหน้า (ไม่จำเป็นต้องจำกฎลำดับความสำคัญ)
  • ไม่บริสุทธิ์จึงเหมาะสำหรับการสร้างต้นแบบอย่างรวดเร็ว
  • ระบบวัตถุที่มีประสิทธิภาพพร้อมโปรโตคอลเมตาวัตถุ
  • มาตรฐานผู้ใหญ่
  • คอมไพเลอร์ที่หลากหลาย

แน่นอนว่า Haskell มีข้อดีของตัวเองและทำบางอย่างด้วยวิธีที่แตกต่างกันโดยพื้นฐาน แต่ก็ไม่ได้ลดลงในระยะยาวสำหรับฉัน


เฮ้คุณมีชื่อของกระดาษ Costanza ที่คุณเชื่อมโยงด้วยหรือไม่? ดูเหมือนว่าไฟล์นั้นถูกย้าย
michiakig

2
โปรดทราบว่า haskell สนับสนุนไวยากรณ์คำนำหน้าเช่นกัน แต่ฉันจะบอกว่า monad >> = น่าเกลียดมากเมื่อใช้มัน นอกจากนี้ฉันไม่เห็นด้วยกับสิ่งที่ไม่บริสุทธิ์เป็นพร: P
ทางเลือก

2
ฉันชอบบันทึกด้านข้างนี้: เรายังไม่ได้รวบรวมข้อมูลเชิงประจักษ์ว่าปัญหานี้ทำให้เกิดปัญหาร้ายแรงในโปรแกรมจริงหรือไม่
Jerome Baum

22
ไม่มีตัวอย่างใดในเอกสารนั้น (Pascal Costanza, Dynamic vs. Static Typing - A Pattern-Based Analysis ) ใช้กับ Haskell ทั้งหมดเป็นเฉพาะ Java (หรือเฉพาะเจาะจงสำหรับ"การเขียนโปรแกรมเชิงวัตถุ" ) และฉันไม่เห็นปัญหาใด ๆ ที่เกิดขึ้นใน Haskell ในทำนองเดียวกันข้อโต้แย้งอื่น ๆ ของคุณก็เป็นที่ถกเถียงกันได้เช่นกันใคร ๆ ก็บอกได้เช่นกันว่า Haskell นั้น "บริสุทธิ์และเหมาะกว่าสำหรับการสร้างต้นแบบอย่างรวดเร็ว" ไวยากรณ์คำนำหน้านั้นไม่ได้บังคับเพราะไม่มีคอมไพเลอร์หลากหลายประเภทที่ทำสิ่งต่างๆ ฯลฯ
ShreevatsaR

11
กระดาษแผ่นนั้นแทบไม่เกี่ยวข้องกับ Haskell เลย " dilbert = dogbert.hire(dilbert);" ?? ฉันสงสัยว่าโปรแกรมเมอร์ของ Haskell หลายคนสามารถอ่านสิ่งนี้ได้โดยไม่กระตุกเลยแม้แต่น้อย
leftaround ประมาณ

6

ใน Haskell คุณสามารถกำหนดฟังก์ชัน if ซึ่งเป็นไปไม่ได้ใน LISP สิ่งนี้เป็นไปได้เนื่องจากความเกียจคร้านซึ่งทำให้โปรแกรมมีความเป็นโมดูลมากขึ้น เอกสารคลาสสิกนี้: ทำไม FP จึงสำคัญโดย John Hughes อธิบายว่าความเกียจคร้านช่วยเพิ่มความสามารถในการประกอบได้อย่างไร


5
โครงการ (หนึ่งในสองภาษาหลักของ LISP) มีการประเมินแบบเกียจคร้านแม้ว่าจะไม่ใช่ค่าเริ่มต้นเหมือนใน Haskell
Randy Voet

7
(defmacro doif (xy) `(if, x, y))
Joshua Cheek

4
แมโครไม่ได้เป็นเช่นเดียวกับฟังก์ชั่น - แมโครไม่ทำงานได้ดีกับฟังก์ชั่นขั้นสูงเหมือนfoldเช่นในขณะที่ฟังก์ชั่นที่ไม่เข้มงวดทำ
Tikhon Jelvis

5

มีสิ่งดีๆที่คุณสามารถทำได้ใน Lisp ด้วยมาโครที่ยุ่งยาก (ถ้าเป็นไปได้) ใน Haskell ยกตัวอย่างมาโคร `` บันทึก '' (ดูบทที่ 9 ของ PAIP ของ Peter Norvig) ด้วยวิธีนี้คุณสามารถกำหนดฟังก์ชันพูด foo แล้วประเมิน (บันทึก 'foo) ซึ่งแทนที่คำจำกัดความสากลของ foo ด้วยเวอร์ชันบันทึก คุณสามารถบรรลุเอฟเฟกต์เดียวกันใน Haskell ด้วยฟังก์ชันลำดับที่สูงกว่าได้หรือไม่?


3
ไม่มาก (AFAIK) แต่คุณสามารถทำสิ่งที่คล้ายกันได้โดยการปรับเปลี่ยนฟังก์ชัน (สมมติว่าเป็นแบบวนซ้ำ) เพื่อใช้ฟังก์ชันเพื่อเรียกแบบวนซ้ำเป็นพารามิเตอร์ (!) แทนที่จะเรียกตัวเองด้วยชื่อ: haskell.org/haskellwiki/Memoization
j_random_hacker

6
คุณสามารถเพิ่ม foo ให้กับโครงสร้างข้อมูลที่ขี้เกียจซึ่งค่าจะถูกจัดเก็บเมื่อคำนวณแล้ว สิ่งนี้จะมีประสิทธิภาพเหมือนกัน
Theo Belaire

ทุกอย่างใน Haskell จะถูกบันทึกไว้และอาจมีการแทรกในบรรทัดเมื่อจำเป็นโดยค่าเริ่มต้นโดยคอมไพเลอร์ Haskell
aoeu256

4

ขณะที่ฉันเดินทางต่อไปเพื่อเรียนรู้ Haskell ดูเหมือนว่าสิ่งหนึ่งที่ช่วย "แทนที่" มาโครได้คือความสามารถในการกำหนดตัวดำเนินการ infix ของคุณเองและกำหนดลำดับความสำคัญและการเชื่อมโยงของมันเอง ค่อนข้างซับซ้อน แต่เป็นระบบที่น่าสนใจ!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.