อะไรทำให้ระบบประเภท Haskell ได้รับการเคารพนับถืออย่างมาก (vs say, Java)


204

ฉันเริ่มที่จะเรียนรู้Haskell ฉันยังใหม่กับมันมากและฉันเพิ่งอ่านหนังสือออนไลน์สองสามเล่มเพื่อให้เข้าใจถึงโครงสร้างพื้นฐานของหนังสือเล่มนี้

หนึ่งใน 'มส์' ที่คนคุ้นเคยกับมันมักจะพูดถึงคือ "ถ้ามันรวบรวมมันจะทำงาน *" - ซึ่งฉันคิดว่าเกี่ยวข้องกับความแข็งแกร่งของระบบประเภท

ฉันพยายามที่จะเข้าใจว่าทำไมตรง Haskell จะดีกว่าภาษาพิมพ์แบบคงที่อื่น ๆ ในเรื่องนี้

วางวิธีอื่นผมถือว่าใน Java คุณสามารถทำสิ่งชั่วร้ายเช่นฝังศพ จะมีอะไรบางอย่างที่มันควรจะเป็นArrayList<String>() ArrayList<Animal>()สิ่งที่ชั่วร้ายนี่คือสิ่งที่คุณstringมีelephant, giraffeฯลฯ และถ้ามีคนใส่เข้าไปMercedes- คอมไพเลอร์ของคุณจะไม่ช่วยคุณ

ถ้าผมไม่ได้ทำArrayList<Animal>()แล้วในบางจุดในเวลาต่อมาถ้าผมตัดสินใจที่โปรแกรมของฉันไม่ได้จริงๆเกี่ยวกับสัตว์มันเป็นเรื่องของยานพาหนะแล้วฉันสามารถเปลี่ยนการพูด, ฟังก์ชั่นที่ผลิตArrayList<Animal>ในการผลิตArrayList<Vehicle>และ IDE ของฉันควรจะบอกฉันทุกที่มี เป็นการพักสะสม

ข้อสันนิษฐานของฉันคือว่านี่คือสิ่งที่ผู้คนหมายถึงโดยระบบพิมพ์ที่แข็งแกร่งแต่ไม่ชัดเจนสำหรับฉันว่าทำไม Haskell ถึงดีกว่า อีกวิธีหนึ่งคุณสามารถเขียน Java ที่ดีหรือไม่ดีฉันคิดว่าคุณสามารถทำได้เหมือนกันใน Haskell (เช่นสิ่งต่าง ๆ เป็นสตริง / ints ที่ควรเป็นประเภทข้อมูลชั้นหนึ่ง)

ฉันสงสัยว่าตัวเองขาดอะไร / สำคัญไป
ฉันมีความสุขมากที่ได้เห็นข้อผิดพลาดในวิธีการของฉัน!


31
ฉันจะให้ผู้คนมีความรู้มากกว่าที่ฉันเขียนคำตอบจริง ๆ แต่สิ่งสำคัญของมันคือ: ภาษาที่พิมพ์แบบคงที่เช่น C # มีระบบประเภทที่พยายามช่วยคุณในการเขียนรหัสที่ป้องกันได้ ระบบการพิมพ์เช่นความพยายามของ Haskell ที่จะช่วยคุณเขียนรหัสที่ถูกต้อง (เช่นพิสูจน์ได้) หลักการพื้นฐานในที่ทำงานคือการย้ายสิ่งต่าง ๆ ที่สามารถตรวจสอบได้ในขั้นตอนการคอมไพล์ Haskell ตรวจสอบสิ่งต่างๆ เพิ่มเติมในเวลารวบรวม
Robert Harvey

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

12
ฉันไม่รู้ว่าทำไมคำตอบทั้งหมดถึงพูดMaybeจบเท่านั้น ถ้าฉันต้องเลือกเพียงสิ่งเดียวที่ภาษายอดนิยมควรยืมจาก Haskell นี่จะเป็นเช่นนั้น มันเป็นแนวคิดที่ง่ายมาก (ไม่ค่อยน่าสนใจเท่าที่ควรจากมุมมองทางทฤษฎี) แต่เพียงอย่างเดียวนี้จะทำให้งานของเราง่ายขึ้นมาก
พอล

1
จะมีคำตอบที่ดีที่นี่ แต่ในความพยายามที่จะช่วยเหลือให้ศึกษาประเภทของลายเซ็น พวกเขาอนุญาตให้มนุษย์และโปรแกรมให้เหตุผลเกี่ยวกับโปรแกรมในลักษณะที่จะแสดงให้เห็นว่า Java อยู่ในประเภทสื่อกลางอย่างไร
Michael Easter

6
เพื่อความเป็นธรรมฉันต้องชี้ให้เห็นว่า "ทั้งหมดถ้ามันรวบรวมมันจะได้ผล" เป็นสโลแกนที่ไม่ใช่คำแถลงที่แท้จริง ใช่เราโปรแกรมเมอร์ Haskell รู้ว่าการผ่านตัวตรวจสอบชนิดจะให้โอกาสที่ถูกต้องสำหรับความคิดที่ถูก จำกัด บางประการ แต่แน่นอนว่าไม่ใช่คำที่ "จริง" อย่างแท้จริงและเป็นสากล!
Tom Ellis

คำตอบ:


230

นี่คือรายการคุณลักษณะระบบชนิดที่ไม่มีการเรียงลำดับที่มีอยู่ใน Haskell และอาจใช้งานไม่ได้หรือไม่ดีใน Java (สำหรับความรู้ของฉันซึ่งเป็นที่ยอมรับว่าอ่อนแอ wrt Java)

  • ความปลอดภัย ประเภทของ Haskell มีคุณสมบัติ "ความปลอดภัยประเภท" ที่ค่อนข้างดี นี่เป็นสิ่งที่เฉพาะเจาะจง แต่โดยพื้นฐานแล้วหมายความว่าค่าในบางประเภทไม่สามารถแปลงเป็นประเภทอื่นได้อย่างซุกซน บางครั้งสิ่งนี้ขัดแย้งกับความไม่แน่นอน (ดูข้อ จำกัด ค่าของ OCaml )
  • ประเภทพีชคณิตข้อมูล ประเภทใน Haskell มีโครงสร้างเดียวกับคณิตศาสตร์ระดับมัธยม สิ่งนี้เป็นเรื่องที่เรียบง่ายและสอดคล้องกันอย่างมาก แต่ก็มีประสิทธิภาพเท่าที่คุณต้องการ มันเป็นรากฐานที่ดีเยี่ยมสำหรับระบบการพิมพ์
    • การเขียนโปรแกรมประเภทข้อมูลทั่วไป สิ่งนี้ไม่เหมือนกับประเภททั่วไป (ดูการวางนัยทั่วไป ) แต่เนื่องจากความเรียบง่ายของโครงสร้างประเภทตามที่ระบุไว้ก่อนหน้านี้ค่อนข้างง่ายในการเขียนรหัสซึ่งทำงานโดยทั่วไปเหนือโครงสร้างนั้น หลังจากนั้นฉันพูดถึงสิ่งที่ชอบEquality อาจได้มาโดยอัตโนมัติสำหรับประเภทที่ผู้ใช้กำหนดโดยคอมไพเลอร์ Haskell โดยพื้นฐานแล้ววิธีการทำสิ่งนี้คือการเดินข้ามโครงสร้างธรรมดาที่เรียบง่ายที่มีพื้นฐานที่ผู้ใช้กำหนดเองและจับคู่ระหว่างค่าต่าง ๆ ซึ่งเป็นรูปแบบที่เป็นธรรมชาติของความเท่าเทียมกันทางโครงสร้าง
  • ประเภท recursive ร่วมกัน นี่เป็นเพียงองค์ประกอบที่สำคัญของการเขียนประเภทที่ไม่สำคัญ
    • ประเภทที่ซ้อนกัน สิ่งนี้ช่วยให้คุณสามารถกำหนดประเภทแบบเรียกซ้ำผ่านตัวแปรที่เรียกคืนในประเภทที่แตกต่างกัน data Bt a = Here a | There (Bt (a, a))ยกตัวอย่างเช่นหนึ่งในประเภทของต้นไม้สมดุลเป็น คิดให้รอบคอบเกี่ยวกับค่าที่ถูกต้องBt aและสังเกตว่าการทำงานประเภทนั้นอย่างไร มันยากมาก!
  • ลักษณะทั่วไป มันเกือบจะโง่เกินไปที่จะไม่มีในระบบพิมพ์ (อะแฮ่มมองคุณไป) สิ่งสำคัญคือต้องมีความคิดเกี่ยวกับตัวแปรประเภทและความสามารถในการพูดคุยเกี่ยวกับโค้ดซึ่งไม่ขึ้นอยู่กับตัวเลือกของตัวแปรนั้น Hindley มิลเนอร์เป็นระบบชนิดซึ่งได้มาจากระบบการพิมพ์ระบบเอฟของ Haskell เป็นรายละเอียดของ HM พิมพ์และระบบ F เป็นหลักครอบครัวของทั่วไป สิ่งที่ฉันหมายถึงคือ Haskell มีเรื่องราวทั่วไปที่ดีมาก
  • ประเภทบทคัดย่อ เรื่องราวของ Haskell ที่นี่ไม่ดี แต่ก็ไม่ได้มีอยู่จริง เป็นไปได้ที่จะเขียนชนิดที่มีส่วนต่อประสานสาธารณะ แต่เป็นการใช้งานส่วนตัว นี้ช่วยให้เราทั้งสองยอมรับการเปลี่ยนแปลงรหัสการดำเนินการในเวลาต่อมาและที่สำคัญเนื่องจากเป็นพื้นฐานของการดำเนินงานทั้งหมดใน Haskell เขียน "มายากล" IOชนิดที่มีอินเตอร์เฟซที่ดีที่กำหนดเช่น Java อาจมีเรื่องเล่าที่เป็นนามธรรมมากกว่าจริง แต่ฉันไม่คิดจนกระทั่งการเชื่อมต่อที่เป็นที่นิยมมากขึ้นนั้นเป็นเรื่องจริง
  • Parametricity ค่า Haskell ไม่มีการดำเนินงานสากลใด ๆ Java ละเมิดสิ่งนี้ด้วยสิ่งต่าง ๆ เช่นความเท่าเทียมกันของข้อมูลอ้างอิงและการแฮชและมีการบังคับข่มขู่ สิ่งนี้หมายความว่าคุณจะได้รับทฤษฏีอิสระเกี่ยวกับประเภทที่อนุญาตให้คุณรู้ความหมายของการดำเนินการหรือคุณค่าในระดับที่น่าทึ่งโดยสิ้นเชิงจากประเภทของมัน --- บางประเภทเป็นเช่นนั้นที่จะมีประชากรเพียงเล็กน้อยเท่านั้น
  • ประเภทที่สูงกว่าจะแสดงประเภททั้งหมดเมื่อเข้ารหัสสิ่งที่มีเล่ห์เหลี่ยม Functor / Applicative / Monad, แบบพับได้ / หมุนได้mtl, ระบบการพิมพ์เอฟเฟกต์ทั้งหมด, จุดพักฟื้นแบบ Functor ทั่วไป รายการไปบนและบน. มีหลายสิ่งหลายอย่างที่แสดงออกได้ดีที่สุดในประเภทที่สูงกว่าและระบบประเภทที่ค่อนข้างน้อยยังอนุญาตให้ผู้ใช้พูดคุยเกี่ยวกับสิ่งเหล่านี้
  • เรียนประเภท หากคุณคิดว่าระบบการพิมพ์เป็น logics ซึ่งมีประโยชน์คุณก็มักจะถูกขอให้พิสูจน์สิ่งต่าง ๆ ในหลายกรณีนี่คือสัญญาณรบกวนสาย: อาจมีเพียงคำตอบเดียวเท่านั้นและเสียเวลาและความพยายามในการเขียนโปรแกรมนี้ Typeclasses เป็นวิธีสำหรับ Haskell เพื่อสร้างบทพิสูจน์สำหรับคุณ ในแง่ที่เป็นรูปธรรมมากขึ้นสิ่งนี้จะช่วยให้คุณแก้ปัญหา "ระบบสมการประเภท" แบบง่าย ๆ "เช่น" เราต้องการที่จะทำ(+)สิ่งต่าง ๆ ด้วยกันแบบไหนกันโอ้Integerวตกลงกันเลยใส่โค้ดที่ถูกต้องตอนนี้! " ในระบบที่ซับซ้อนมากขึ้นคุณอาจกำลังสร้างข้อ จำกัด ที่น่าสนใจมากขึ้น
    • แคลคูลัส จำกัด ข้อ จำกัด ใน Haskell - ซึ่งเป็นกลไกในการเข้าถึงระบบอารัมภบทประเภทพิมพ์ดีด สิ่งนี้ให้ความสัมพันธ์ของการพิมพ์ย่อยที่ง่ายมากซึ่งช่วยให้คุณรวบรวมข้อ จำกัด ที่ซับซ้อนจากสิ่งที่ง่ายกว่า mtlห้องสมุดทั้งหมดขึ้นอยู่กับความคิดนี้
    • สืบมา เพื่อที่จะขับรถcanonicityระบบ typeclass มันจำเป็นในการเขียนมากรหัสมักเล็กน้อยเพื่ออธิบายข้อ จำกัด ประเภทที่ผู้ใช้กำหนดต้องยกตัวอย่าง ทำกับโครงสร้างที่ธรรมดามากของประเภท Haskell มันมักจะเป็นไปได้ที่จะขอให้คอมไพเลอร์ทำแผ่นความร้อนนี้ให้คุณ
    • เปิดฉากระดับประเภท Haskell type class solver— ระบบที่สร้าง "หลักฐาน" เหล่านั้นที่ฉันอ้างถึงก่อนหน้านี้ - โดยพื้นฐานแล้วเป็นรูปแบบที่ผิดเพี้ยนของ Prolog ที่มีคุณสมบัติความหมายที่ดีกว่า ซึ่งหมายความว่าคุณสามารถเข้ารหัสสิ่งที่มีขนดกในรูปแบบของอารัมภบทและคาดว่าพวกเขาจะได้รับการจัดการทั้งหมดในเวลารวบรวม ตัวอย่างที่ดีอาจแก้ไขเพื่อพิสูจน์ว่ารายการที่ต่างกันสองรายการมีค่าเท่ากันถ้าคุณลืมลำดับ - พวกมันเทียบเท่ากับ "เซต" ที่ต่างกัน
    • multi-parameter เรียนประเภทและการอ้างอิงการทำงาน สิ่งเหล่านี้เป็นเพียงการปรับแต่งที่มีประโยชน์อย่างยิ่งสำหรับโปรล็อกพื้นฐานของ typeclass ถ้าคุณรู้จัก Prolog คุณสามารถจินตนาการได้ว่าพลังการแสดงออกเพิ่มขึ้นเมื่อคุณสามารถเขียนภาคแสดงของตัวแปรมากกว่าหนึ่งตัว
  • อนุมานสวยดี ภาษาที่ใช้ระบบประเภท Hindley Milner มีการอนุมานที่ดี HM เองมีข้อสรุปที่สมบูรณ์ซึ่งหมายความว่าคุณไม่จำเป็นต้องเขียนตัวแปรชนิด Haskell 98 รูปแบบที่ง่ายที่สุดของ Haskell แล้วโยนมันออกไปในบางสถานการณ์ที่หายากมาก โดยทั่วไป Haskell ที่ทันสมัยได้รับการทดลองในการลดพื้นที่การอนุมานที่สมบูรณ์อย่างช้าๆในขณะที่เพิ่มพลังให้กับ HM และเมื่อผู้ใช้บ่น คนไม่ค่อยบ่น - การอนุมานของ Haskell ค่อนข้างดี
  • มากมาก subtyping อ่อนแอมากเท่านั้น ฉันได้กล่าวไปแล้วก่อนหน้านี้ว่าระบบข้อ จำกัด จากคำนำประเภท typeclass มีความคิดเกี่ยวกับการพิมพ์ย่อยโครงสร้าง นั่นคือรูปแบบเฉพาะของ subtyping ใน Haskell การพิมพ์ย่อยนั้นแย่มากสำหรับการใช้เหตุผลและการอนุมาน มันทำให้แต่ละปัญหานั้นยากขึ้นอย่างมาก (ระบบของความไม่เท่าเทียมกันแทนที่จะเป็นระบบของความเท่าเทียมกัน) นอกจากนี้ยังเข้าใจผิดได้ง่าย ๆ (subclassing เหมือนกับ subtyping หรือไม่ไม่แน่นอน! แต่ผู้คนสับสนบ่อยมากและหลายภาษาช่วยในความสับสนนั้น! เราลงเอยที่นี่ได้อย่างไรฉันคิดว่าไม่มีใครเคยตรวจสอบ LSP)
    • หมายเหตุเมื่อเร็ว ๆ นี้ (ต้นปี 2017) Steven Dolan เผยแพร่วิทยานิพนธ์ของเขาในMLsubตัวแปรของการอนุมานประเภท ML และ Hindley-Milner ซึ่งมีเรื่องราวย่อยที่ดีมาก ( ดูเพิ่มเติม ) สิ่งนี้ไม่ได้ลบล้างสิ่งที่ฉันเขียนด้านบน - ระบบย่อยส่วนใหญ่แตกหักและมีการอนุมานไม่ดี - แต่แนะนำว่าเราเพิ่งจะได้ค้นพบวิธีที่มีแนวโน้มว่าจะมีการอนุมานและการเล่นย่อยร่วมกันอย่างสมบูรณ์ ตอนนี้เพื่อความชัดเจนโดยสิ้นเชิงแนวคิดเกี่ยวกับการพิมพ์ย่อยของ Java นั้นไม่สามารถใช้ประโยชน์จากอัลกอริทึมและระบบของ Dolan ได้ มันต้องมีการคิดใหม่เกี่ยวกับความหมายของการพิมพ์ย่อย
  • ประเภทตำแหน่งที่สูงขึ้น ผมได้พูดคุยเกี่ยวกับลักษณะทั่วไปก่อนหน้านี้ แต่ที่มากกว่าเพียงแค่ทั่วไปเพียงมันเป็นประโยชน์เพื่อให้สามารถที่จะพูดคุยเกี่ยวกับประเภทที่ได้ทั่วไปตัวแปรภายในพวกเขา ยกตัวอย่างเช่นการทำแผนที่ระหว่างโครงสร้างการสั่งซื้อที่สูงขึ้นซึ่งเป็นลบเลือน (ดูparametricity ) กับสิ่งที่เค้าเหล่านั้น "มี" (forall a. f a -> g a)มีประเภทเช่น ในตรงหือคุณสามารถเขียนฟังก์ชั่นที่เป็นแบบนี้ แต่มีชนิดที่อันดับสูงกว่าที่คุณเรียกร้องเช่นฟังก์ชั่นเป็นอาร์กิวเมนต์mapFree :: (forall a . f a -> g a) -> Free f -> Free gชอบโดย: ขอให้สังเกตว่าaตัวแปรถูกผูกไว้ภายในอาร์กิวเมนต์เท่านั้น ซึ่งหมายความว่าdefinerของฟังก์ชั่นmapFreeได้รับการตัดสินใจเลือกสิ่งที่ถูกสร้างในเมื่อพวกเขาใช้มันไม่ใช้ของamapFree
  • ประเภทที่มีอยู่ ในขณะที่ประเภทอันดับที่สูงขึ้นช่วยให้เราสามารถพูดคุยเกี่ยวกับปริมาณสากลประเภทอัตถิภาวนิยมให้เราพูดคุยเกี่ยวกับปริมาณอัตถิภาวนิยม: ความคิดที่ว่ามีเพียงที่มีอยู่บางชนิดที่ไม่รู้จักความพึงพอใจของสมการบางอย่าง การทำสิ่งนี้ให้เป็นประโยชน์และการดำเนินต่อไปเป็นเวลานานจะใช้เวลานาน
  • ครอบครัวประเภท บางครั้งกลไกพิมพ์ดีดไม่สะดวกเนื่องจากเราไม่ได้คิดใน Prolog เสมอไป ตระกูลแบบให้เราเขียนความสัมพันธ์การทำงานระหว่างประเภทโดยตรง
    • ครอบครัวประเภทปิดแล้ว ตระกูลแบบเปิดโดยค่าเริ่มต้นซึ่งน่ารำคาญเพราะมันหมายความว่าในขณะที่คุณสามารถขยายพวกเขาได้ตลอดเวลาคุณไม่สามารถ "กลับ" พวกมันด้วยความหวังใด ๆ ที่จะประสบความสำเร็จ นี่เป็นเพราะคุณไม่สามารถพิสูจน์ความสามารถในการฉีดยาได้ แต่ด้วยตระกูลแบบปิดคุณสามารถทำได้
  • ประเภทการจัดทำดัชนีชนิดและส่งเสริมการขายประเภท ฉันเริ่มแปลกใหม่จริงๆ ณ จุดนี้ แต่สิ่งเหล่านี้มีประโยชน์ในการใช้งานเป็นครั้งคราว หากคุณต้องการที่จะเขียนประเภทของการจัดการที่เปิดหรือปิดจากนั้นคุณสามารถทำได้อย่างมาก โปรดสังเกตในตัวอย่างต่อไปนี้ซึ่งStateเป็นประเภทพีชคณิตที่ง่ายมากซึ่งมีค่าที่เลื่อนระดับเป็นระดับประเภทด้วย จากนั้นต่อมาเราสามารถพูดคุยเกี่ยวกับประเภทการก่อสร้างเช่นHandleกับการโต้แย้งได้อย่างเฉพาะเจาะจงชนิดStateเช่น มันสับสนที่จะเข้าใจรายละเอียดทั้งหมด แต่ก็ถูกต้องมาก

    data State = Open | Closed
    
    data Handle :: State -> * -> * where
      OpenHandle :: {- something -} -> Handle Open a
      ClosedHandle :: {- something -} -> Handle Closed a
    
  • การแสดง Runtime ประเภทที่ทำงาน Java เป็นที่รู้จักกันดีว่ามีการลบประเภทและมีฝนตกในขบวนพาเหรดของบางคน การลบประเภทเป็นวิธีที่เหมาะสมที่จะไปอย่างไรก็ตามราวกับว่าคุณมีฟังก์ชั่นgetRepr :: a -> TypeReprดังนั้นคุณจะต้องมีการละเมิดพารามิเตอร์น้อยที่สุด สิ่งที่แย่กว่านั้นก็คือถ้าเป็นฟังก์ชั่นที่ผู้ใช้สร้างขึ้นซึ่งใช้ในการกระตุ้นการข่มขู่ที่ไม่ปลอดภัยในขณะทำงาน ... จากนั้นคุณก็มีข้อกังวลด้านความปลอดภัยมากมาย Typeableระบบของ Haskell ช่วยให้สามารถสร้างตู้นิรภัยcoerce :: (Typeable a, Typeable b) => a -> Maybe bได้ ระบบนี้อาศัยTypeableการใช้งานในคอมไพเลอร์ (ไม่ใช่ผู้ใช้) และไม่สามารถให้ความหมายที่ดีเช่นนี้ได้หากปราศจากกลไกพิมพ์ดีดของ Haskell และกฎหมายที่รับประกันว่าจะปฏิบัติตาม

มากกว่าสิ่งเหล่านี้อย่างไรก็ตามคุณค่าของระบบประเภทของ Haskell ยังเกี่ยวข้องกับวิธีที่ประเภทนั้นอธิบายภาษา นี่คือคุณสมบัติบางอย่างของ Haskell ซึ่งมีค่าไดรฟ์ผ่านระบบประเภท

  • ความบริสุทธิ์ Haskell ไม่อนุญาตให้มีผลข้างเคียงสำหรับ "ผลข้างเคียง" ที่กว้างมาก ๆ สิ่งนี้บังคับให้คุณใส่ข้อมูลเพิ่มเติมลงในประเภทเนื่องจากประเภทจะควบคุมอินพุตและเอาต์พุตและไม่มีผลข้างเคียงทุกอย่างจะต้องนำมาคิดในอินพุตและเอาต์พุต
    • IO ต่อจากนั้น Haskell ต้องการวิธีการพูดคุยเกี่ยวกับผลข้างเคียง - เนื่องจากโปรแกรมจริง ๆ ต้องมีบางอย่าง - ดังนั้นการรวมกันของ typeclasses ชนิดที่สูงกว่าและประเภทที่เป็นนามธรรมทำให้เกิดความคิดในการใช้ชนิดพิเศษพิเศษที่เรียกว่าIO aเป็นตัวแทน aการคำนวณด้านมีผลซึ่งส่งผลให้ค่าประเภท นี่คือรากฐานของระบบเอฟเฟกต์ที่ดีมากซึ่งฝังอยู่ภายในภาษาบริสุทธิ์
  • nullขาด ทุกคนรู้ว่านั่นnullเป็นความผิดพลาดพันล้านดอลลาร์ของภาษาการเขียนโปรแกรมที่ทันสมัย ประเภทพีชคณิตโดยเฉพาะอย่างยิ่งความสามารถในการผนวกเพียงแค่ "ไม่อยู่" รัฐไปยังประเภทที่คุณมีโดยการเปลี่ยนชนิดAลงไปในชนิดสมบูรณ์บรรเทาปัญหาของMaybe Anull
  • recursion polymorphic สิ่งนี้ช่วยให้คุณกำหนดฟังก์ชั่นแบบเรียกซ้ำซึ่งสรุปลักษณะของตัวแปรประเภทต่าง ๆ แม้จะใช้กับชนิดที่ต่างกันในการเรียกแบบเรียกซ้ำในลักษณะทั่วไปของตัวเอง นี่เป็นเรื่องยากที่จะพูดคุย แต่มีประโยชน์อย่างยิ่งสำหรับการพูดคุยเกี่ยวกับประเภทซ้อนกัน มองย้อนกลับไปที่ประเภทจากก่อนและพยายามที่จะเขียนฟังก์ชั่นการคำนวณขนาดของมัน:Bt a size :: Bt a -> Intมันจะมีลักษณะบิตเช่นและsize (Here a) = 1 size (There bt) = 2 * size btการดำเนินงานที่ไม่ซับซ้อนเกินไป แต่สังเกตเห็นว่าโทร recursive sizeในสมการที่ผ่านมาเกิดขึ้นในประเภทที่แตกต่างกันsize :: Bt a -> Intแต่ความหมายโดยรวมมีประเภททั่วไปดี โปรดทราบว่านี่เป็นคุณสมบัติที่แยกการอนุมานทั้งหมด แต่ถ้าคุณระบุลายเซ็นประเภท Haskell จะอนุญาต

ฉันจะไปต่อ แต่รายการนี้ควรจะให้คุณเริ่มต้นแล้ว - บางส่วน


7
Null ไม่ใช่ "ความผิดพลาด" พันล้านดอลลาร์ มีหลายกรณีที่มันไม่ได้เป็นไปได้ที่จะเป็นแบบคงที่ตรวจสอบตัวชี้ที่จะไม่ถูก dereferenced ก่อนที่จะมีความหมายอาจจะมีอยู่ ; การมีกับดักที่พยายามทำการอ้างอิงในกรณีเช่นนี้มักจะดีกว่ากำหนดให้ตัวชี้เริ่มระบุวัตถุที่ไม่มีความหมาย ฉันคิดว่าข้อผิดพลาดที่เกี่ยวข้องกับ null ที่ใหญ่ที่สุดคือมีการใช้งานที่กำหนดchar *p = NULL;จะดักต่อ*p=1234แต่จะไม่ดักจับchar *q = p+5678;หรือ*q = 1234;
supercat

37
นั่นเป็นเพียงยกมาตรงจากโทนีฮอร์: en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions ในขณะที่ฉันแน่ใจว่ามีบางครั้งที่nullมีความจำเป็นในการคำนวณทางคณิตศาสตร์ตัวชี้ แต่ฉันตีความที่จะบอกว่าการคำนวณทางคณิตศาสตร์ชี้เป็นสถานที่ที่ไม่ดีในการโฮสต์ความหมายของภาษาของคุณไม่ใช่ null ที่ไม่ผิด
J. Abrahamson

18
@supercat คุณสามารถเขียนภาษาได้โดยไม่เป็นโมฆะ ไม่ว่าจะอนุญาตหรือไม่เป็นทางเลือก
พอลเดรเปอร์

6
@supercat - ปัญหานั้นมีอยู่ใน Haskell ด้วย แต่มีรูปแบบที่แตกต่างออกไป โดยทั่วไปแล้ว Haskell จะขี้เกียจและไม่เปลี่ยนรูปดังนั้นคุณจึงสามารถเขียนได้p = undefinedตราบใดที่pไม่ได้รับการประเมิน มีประโยชน์มากขึ้นคุณสามารถใส่undefinedการอ้างอิงที่ไม่แน่นอนบางประเภทได้อีกครั้งตราบใดที่คุณไม่ได้ประเมิน ความท้าทายที่รุนแรงยิ่งขึ้นคือการคำนวณที่ขี้เกียจซึ่งอาจไม่ยุติซึ่งแน่นอนว่าไม่สามารถตัดสินใจได้ ความแตกต่างที่สำคัญคือสิ่งเหล่านี้ล้วนเป็นความผิดพลาดของการเขียนโปรแกรมอย่างไม่น่าสงสัยและไม่เคยถูกใช้ในการแสดงตรรกะสามัญ
Christian Conkle

6
@supercat Haskell ไม่มีความหมายของการอ้างอิงทั้งหมด (นี่คือแนวคิดของความโปร่งใสในการอ้างอิงซึ่งหมายถึงทุกอย่างถูกเก็บรักษาไว้โดยแทนที่การอ้างอิงด้วยการอ้างอิงของพวกเขา) ดังนั้นฉันคิดว่าคำถามของคุณไม่ถูกต้อง
J. Abrahamson

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

7
คำตอบที่ดี - คุณช่วยให้ฉันเป็นตัวอย่างที่ง่ายขึ้นของชนิดที่สูงกว่าคิดว่าจะช่วยให้ฉันเข้าใจว่าทำไมมันเป็นไปไม่ได้ที่จะทำในจาวา
phatmanace

3
มีบางตัวอย่างที่ดีอยู่ที่นี่
Karl Bielefeldt

3
การจับคู่รูปแบบเป็นสิ่งที่สำคัญมากเช่นกันหมายความว่าคุณสามารถใช้ชนิดของวัตถุเพื่อทำการตัดสินใจได้อย่างง่ายดาย
Benjamin Gruenbaum

2
@BenjaminGruenbaum ฉันไม่คิดว่าฉันจะเรียกมันว่าคุณสมบัติของระบบ
Doval

3
ในขณะที่ ADT และ HKT เป็นส่วนหนึ่งของคำตอบอย่างแน่นอนฉันสงสัยว่าใครก็ตามที่ถามคำถามนี้จะรู้ว่าทำไมพวกเขาถึงมีประโยชน์ฉันขอแนะนำว่าทั้งสองส่วนจำเป็นต้องขยายเพื่ออธิบายสิ่งนี้
jk

62
a :: Integer
b :: Maybe Integer
c :: IO Integer
d :: Either String Integer

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

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


11
+1 ในขณะที่คำตอบนี้ยังไม่สมบูรณ์ฉันคิดว่ามันดีกว่าระดับเสียงของคำถาม
jk

1
+1 แม้ว่ามันจะช่วยอธิบายว่าภาษาอื่นมีMaybe(เช่น Java Optionalและ Scala's Option) แต่ในภาษาเหล่านั้นมันเป็นวิธีการอบครึ่งเนื่องจากคุณสามารถกำหนดให้nullกับตัวแปรประเภทนั้นและทำให้โปรแกรมของคุณระเบิดเมื่อรัน - เวลา. สิ่งนี้ไม่สามารถเกิดขึ้นได้กับ Haskell [1] เนื่องจากไม่มีค่าว่างดังนั้นคุณจึงไม่สามารถโกงได้ ([1]: จริง ๆ แล้วคุณสามารถสร้างข้อผิดพลาดที่คล้ายกันกับ NullPointerException โดยใช้ฟังก์ชั่นบางส่วนเช่นfromJustเมื่อคุณมีNothingแต่ฟังก์ชั่นเหล่านั้นอาจขมวดคิ้วอยู่)
Andres F.

2
"จำนวนเต็มที่มีค่ามาจากโลกภายนอก" - จะไม่IO Integerใกล้เคียงกับ 'โปรแกรมย่อยซึ่งเมื่อประมวลผลจะให้จำนวนเต็ม' a) ในmain = c >> cค่าที่ส่งคืนโดยอันดับแรกcอาจแตกต่างกันไปในแต่ละวินาทีcในขณะที่aจะมีค่าเดียวกันโดยไม่คำนึงถึงตำแหน่ง (ตราบเท่าที่เราอยู่ในขอบเขตเดียว) b) มีประเภทที่แสดงถึงคุณค่าจากโลกภายนอกเพื่อบังคับใช้ความพึงพอใจ (เช่นไม่ให้ใส่โดยตรง แต่ตรวจสอบก่อนว่าข้อมูลจากผู้ใช้ถูกต้องหรือไม่เป็นอันตราย)
Maciej Piechotka

4
Maciej นั่นจะแม่นยำยิ่งขึ้น ฉันพยายามอย่างหนักเพื่อความเรียบง่าย
WolfeFan

30

ผู้คนมากมายได้แสดงรายการสิ่งที่ดีเกี่ยวกับ Haskell แต่ในการตอบคำถามเฉพาะของคุณ "ทำไมระบบประเภททำให้โปรแกรมถูกต้องมากขึ้น?" ฉันสงสัยว่าคำตอบคือ "พาราเมตริกเมทริกซ์"

พิจารณาฟังก์ชัน Haskell ต่อไปนี้:

foobar :: x -> y -> y

มีตัวอักษรเพียงวิธีหนึ่งที่เป็นไปได้ที่จะใช้ฟังก์ชั่นนี้ เพียงแค่พิมพ์ลายเซ็นฉันสามารถบอกได้อย่างแม่นยำว่าฟังก์ชั่นนี้ทำอะไรเพราะมีเพียงสิ่งเดียวที่เป็นไปได้ที่มันสามารถทำได้ [ตกลงไม่มาก แต่เกือบจะ!]

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

พิจารณาฟังก์ชั่นนี้:

fubar :: Int -> (x -> y) -> y

ฟังก์ชั่นนี้เป็นไปไม่ได้ คุณไม่สามารถใช้ฟังก์ชันนี้ได้อย่างแท้จริง ฉันสามารถบอกได้ว่าแค่จากลายเซ็นประเภท

อย่างที่คุณเห็นลายเซ็นประเภท Haskell จะบอกคุณอย่างมาก!


เปรียบเทียบกับ C # (ขออภัย Java ของฉันมีสนิมเล็กน้อย)

public static TY foobar<TX, TY>(TX in1, TY in2)

มีสองสิ่งที่วิธีนี้ทำได้:

  • ส่งคืนin2ผลลัพธ์
  • วนซ้ำไปตลอดกาลและไม่เคยคืนสิ่งใด
  • โยนข้อยกเว้นและไม่เคยส่งคืนสิ่งใด

จริงๆแล้ว Haskell มีสามตัวเลือกด้วย แต่ C # ยังให้ตัวเลือกเพิ่มเติมแก่คุณ:

  • ส่งคืน null (Haskell ไม่มีค่า null)
  • แก้ไขin2ก่อนส่งคืน (Haskell ไม่มีการแก้ไขในสถานที่)
  • ใช้การสะท้อนกลับ (Haskell ไม่มีการสะท้อนกลับ)
  • ดำเนินการ I / O หลายรายการก่อนส่งคืนผลลัพธ์ (Haskell จะไม่ยอมให้คุณแสดง I / O เว้นแต่คุณจะประกาศว่าคุณแสดง I / O ที่นี่)

การสะท้อนกลับเป็นค้อนที่มีขนาดใหญ่เป็นพิเศษ ใช้การสะท้อนฉันสามารถสร้างTYวัตถุใหม่จากอากาศบางและคืนนั้น! ฉันสามารถตรวจสอบวัตถุทั้งสองและดำเนินการต่าง ๆ ตามสิ่งที่ฉันค้นหา ฉันสามารถทำการดัดแปลงโดยพลการกับวัตถุทั้งสองที่ผ่านเข้ามาได้

I / O เป็นค้อนขนาดใหญ่ที่คล้ายกัน รหัสอาจแสดงข้อความไปยังผู้ใช้หรือเปิดการเชื่อมต่อฐานข้อมูลหรือฟอร์แมตฮาร์ดดิสก์ของคุณหรืออะไรก็ได้


Haskell foobarฟังก์ชั่นโดยคมชัดสามารถเพียงนำข้อมูลบางอย่างและกลับข้อมูลที่ไม่เปลี่ยนแปลง ไม่สามารถ "ดูที่" ข้อมูลได้เนื่องจากชนิดของมันไม่เป็นที่รู้จักในเวลารวบรวม มันไม่สามารถสร้างข้อมูลใหม่เพราะ ... ดีคุณจะสร้างข้อมูลประเภทที่เป็นไปได้อย่างไร คุณต้องการการไตร่ตรองสำหรับสิ่งนั้น ไม่สามารถดำเนินการ I / O ใด ๆ ได้เนื่องจากลายเซ็นประเภทไม่ได้ประกาศว่ากำลังดำเนินการ I / O ดังนั้นจึงไม่สามารถโต้ตอบกับระบบไฟล์หรือเครือข่ายหรือแม้แต่การรันเธรดในโปรแกรมเดียวกัน! (เช่นมันรับประกัน 100% ว่าปลอดภัยต่อเธรด)

อย่างที่คุณเห็นโดยไม่ให้คุณทำสิ่งต่าง ๆ มากมาย Haskell อนุญาตให้คุณรับประกันได้อย่างมั่นใจว่าโค้ดของคุณทำอะไร จริง ๆ แล้วแน่น (สำหรับรหัส polymorphic จริง ๆ ) มีวิธีเดียวเท่านั้นที่เป็นไปได้ที่ชิ้นสามารถเข้าด้วยกัน

(เพื่อความชัดเจน: มันยังคงเป็นไปได้ที่จะเขียนฟังก์ชั่นของ Haskell โดยที่ลายเซ็นประเภทไม่ได้บอกอะไรคุณมากนัก แต่Int -> Intอาจจะเป็นอะไรก็ได้ แต่ถึงอย่างนั้นเราก็รู้ว่าอินพุตเดียวกันนั้นจะสร้างผลลัพธ์เดียวกัน Java ไม่ได้รับประกันว่า!)


4
+1 คำตอบยอดเยี่ยม! นี่เป็นสิ่งที่ทรงพลังมากและมักไม่ค่อยได้รับความสนใจจากผู้มาใหม่ถึง Haskell fubar :: a -> bยังไงก็ตามฟังก์ชั่น "เป็นไปไม่ได้" ที่ง่ายกว่านั้นคือใช่มั้ย (ใช่ฉันรู้unsafeCoerceแล้วฉันถือว่าเราไม่ได้พูดถึงอะไรในชื่อ "ไม่ปลอดภัย" และผู้มาใหม่ไม่ควรกังวลเกี่ยวกับมัน: D)
Andres F.

มีลายเซ็นประเภทที่ง่ายกว่ามากมายที่คุณไม่สามารถเขียนได้ใช่ ตัวอย่างเช่นfoobar :: xunimplementable สวย ...
MathematicalOrchid

ที่จริงแล้วคุณไม่สามารถสร้างเธรดโค้ดที่ไม่ปลอดภัยได้ แต่คุณยังสามารถทำให้มันเป็นแบบมัลติเธรดได้ ตัวเลือกของคุณคือ "ก่อนที่คุณจะประเมินสิ่งนี้ประเมินสิ่งนี้", "เมื่อคุณประเมินสิ่งนี้คุณอาจต้องการประเมินสิ่งนี้ในเธรดแยกต่างหากเช่นกัน" และ "เมื่อคุณประเมินสิ่งนี้คุณอาจต้องการประเมินสิ่งนี้ด้วยเช่นกัน ในชุดข้อความแยก " ค่าเริ่มต้นคือ "ทำตามที่คุณต้องการ" ซึ่งโดยพื้นฐานแล้วหมายถึง "ประเมินผลเร็วที่สุด"
John Dvorak

คุณสามารถเรียกใช้อินสแตนซ์ของ in1 หรือ in2 ที่มีผลข้างเคียงได้มากกว่า หรือคุณสามารถปรับเปลี่ยนสถานะโกลบอล (ซึ่งได้รับอนุญาตถูกจำลองเป็นแอคชั่น IO ใน Haskell แต่อาจไม่ใช่สิ่งที่คนส่วนใหญ่คิดว่าเป็น IO)
Doug McClean

2
@ isomorphismes ประเภทx -> y -> yนี้สามารถนำไปใช้งานได้อย่างสมบูรณ์แบบ ประเภท(x -> y) -> yไม่ได้ ชนิดx -> y -> yใช้สองอินพุตและส่งคืนอินพุตที่สอง ชนิด(x -> y) -> yใช้ฟังก์ชันที่ทำงานอยู่xและอย่างใดต้องทำyจากที่ ...
MathematicalOrchid

17

ที่เกี่ยวข้องกับคำถาม SO

ฉันคิดว่าคุณสามารถทำเช่นเดียวกันใน haskell (เช่นสิ่งต่าง ๆ เป็นสตริง / ints ที่ควรจะเป็นประเภทข้อมูลชั้นแรก)

ไม่คุณไม่สามารถทำได้ - อย่างน้อยก็ไม่ใช่ในลักษณะเดียวกับที่ Java สามารถทำได้ ใน Java สิ่งนี้เกิดขึ้น:

String x = (String)someNonString;

และ Java จะพยายามและไม่ใช้สายอักขระเป็นสตริง Haskell ไม่อนุญาตการเรียงลำดับของสิ่งนี้ขจัดข้อผิดพลาดรันไทม์ทั้งคลาส

nullเป็นส่วนหนึ่งของระบบพิมพ์ (ตามNothing) ดังนั้นจึงต้องมีการขอและจัดการอย่างชัดเจนเพื่อกำจัดข้อผิดพลาดรันไทม์คลาสอื่นทั้งหมด

มีประโยชน์อื่น ๆ อีกมากมายเช่นกันโดยเฉพาะอย่างยิ่งการใช้ซ้ำและประเภทของคลาส - ซึ่งฉันไม่มีความรู้เพียงพอที่จะสื่อสารได้ดี

ส่วนใหญ่เป็นเพราะระบบประเภทของ Haskell ให้การแสดงออกอย่างมาก คุณสามารถทำสิ่งต่าง ๆ ได้มากมายด้วยกฎเพียงไม่กี่ข้อ พิจารณาต้นแฮสเคลล์ที่มีอยู่ในปัจจุบัน:

data Tree a = Leaf a | Branch (Tree a) (Tree a) 

คุณได้กำหนดแผนภูมิแบบทวิภาคทั่วไปทั้งหมด (และตัวสร้างข้อมูลสองตัว) ในโค้ดหนึ่งบรรทัดที่สามารถอ่านได้ ทั้งหมดใช้กฎเพียงไม่กี่ข้อ (มีประเภทผลรวมและประเภทผลิตภัณฑ์ ) นั่นคือไฟล์รหัส 3-4 คลาสและใน Java

โดยเฉพาะอย่างยิ่งในกลุ่มคนที่มีแนวโน้มที่จะกราบไหว้ระบบพิมพ์ความกระชับ / ความสง่างามนี้มีมูลค่าสูง


ฉันเข้าใจเพียง NullPointerExceptions จากคำตอบของคุณ คุณสามารถเพิ่มตัวอย่างเพิ่มเติมได้ไหม
Jesvin Jose

2
ไม่จำเป็นต้องเป็นจริงJLS §5.5.1 : ถ้า T เป็นประเภทคลาสดังนั้นถ้า | S | <: | T | หรือ | T | <: | S |. มิฉะนั้นจะเกิดข้อผิดพลาดในการคอมไพล์เวลา ดังนั้นคอมไพเลอร์จะไม่อนุญาตให้คุณใช้งานสกิลประเภทที่ไม่สามารถย้อนกลับได้
Boris the Spider

ในความคิดของฉันวิธีที่ง่ายที่สุดในการวางข้อได้เปรียบของคลาสประเภทคือพวกเขาเป็นเหมือนinterfaceของที่สามารถเพิ่มเข้าไปในความเป็นจริงและพวกเขาไม่ "ลืม" ประเภทที่ใช้พวกเขา นั่นคือคุณสามารถมั่นใจได้ว่าสองข้อโต้แย้งของฟังก์ชั่นมีประเภทเดียวกันซึ่งแตกต่างจากinterfaces ที่สองList<String>s อาจมีการใช้งานที่แตกต่างกัน ในทางเทคนิคคุณสามารถทำสิ่งที่คล้ายกันมากใน Java โดยการเพิ่มพารามิเตอร์ชนิดลงในทุก ๆ อินเตอร์เฟส แต่ 99% ของอินเทอร์เฟซที่มีอยู่ไม่ทำมันและคุณจะสร้างความสับสนให้กับเพื่อนของคุณ
Doval

2
@BoristheSpider จริง แต่ข้อยกเว้นหล่อมักจะเกี่ยวข้องกับการ downcasting จาก superclass ให้ subclass หรือจากอินเตอร์เฟซชั้นเรียนและก็ไม่ผิดปกติสำหรับ superclass Objectที่จะ
Doval

2
ฉันคิดว่าประเด็นในคำถามเกี่ยวกับสตริงนั้นไม่ได้เกี่ยวข้องกับการแคสต์และข้อผิดพลาดประเภทรันไทม์ แต่ความจริงที่ว่าถ้าคุณไม่ต้องการใช้ประเภทจาวาจะไม่ทำให้คุณ - ในความเป็นจริงการจัดเก็บข้อมูลของคุณในอนุกรม รูปแบบการเหยียดหยามสตริงเป็น Ad-hoc anyประเภท Haskell จะไม่หยุดที่คุณทำเช่นนี้เนื่องจาก ... มันมีสาย Haskell สามารถให้คุณมีเครื่องมือก็ไม่สามารถบังคับให้หยุดคุณจากการทำสิ่งที่โง่ถ้าคุณยืนยันใน Greenspunning เพียงพอของล่ามจะบูรณาการnullในบริบทที่ซ้อนกัน ไม่มีภาษา
Leushenko

0

หนึ่งใน 'มส์' ที่คนคุ้นเคยกับมันมักจะพูดถึงคือ "ถ้ามันรวบรวมมันจะทำงาน *" - ซึ่งฉันคิดว่าเกี่ยวข้องกับความแข็งแกร่งของระบบประเภท

นี่คือความจริงส่วนใหญ่กับโปรแกรมขนาดเล็ก Haskell ป้องกันคุณจากการทำผิดพลาดที่ง่ายในภาษาอื่น (เช่นการเปรียบเทียบInt32a Word32และa และการระเบิดบางอย่าง) แต่มันไม่ได้ป้องกันคุณจากความผิดพลาดทั้งหมด

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

ฉันพยายามเข้าใจว่าทำไม Haskell ถึงดีกว่าภาษาที่พิมพ์แบบคงที่อื่น ๆ ในเรื่องนี้

ประเภทใน Haskell มีน้ำหนักเบาพอสมควรซึ่งเป็นเรื่องง่ายที่จะประกาศประเภทใหม่ ตรงกันข้ามกับภาษาอย่าง Rust ซึ่งทุกอย่างยุ่งยากขึ้นมาหน่อย

สมมติฐานของฉันคือว่านี่คือสิ่งที่ผู้คนหมายถึงโดยระบบที่แข็งแกร่ง แต่มันไม่ชัดเจนสำหรับฉันว่าทำไม Haskell ถึงดีกว่า

Haskell มีคุณสมบัติมากมายนอกเหนือจากผลรวมและประเภทผลิตภัณฑ์ที่เรียบง่าย มันมีประเภทปริมาณในระดับสากล (เช่นid :: a -> a) เช่นกัน คุณยังสามารถสร้างประเภทระเบียนที่มีฟังก์ชั่นซึ่งค่อนข้างแตกต่างจากภาษาเช่น Java หรือ Rust

GHC สามารถสืบทอดบางกรณีตามประเภทเพียงอย่างเดียวและเนื่องจากการกำเนิดของ generics คุณสามารถเขียนฟังก์ชั่นที่ใช้กันทั่วไปในประเภทต่างๆ ค่อนข้างสะดวกและค่อนข้างคล่องแคล่วกว่าภาษาจาวา

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

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

อีกวิธีหนึ่งคุณสามารถเขียน Java ดีหรือไม่ดีฉันคิดว่าคุณสามารถทำเช่นเดียวกันใน Haskell

อาจเป็นเรื่องจริง แต่ก็ขาดจุดสำคัญไปแล้วจุดที่คุณเริ่มถ่ายภาพด้วยตัวเองที่ Haskell นั้นอยู่ไกลเกินกว่าจุดที่คุณเริ่มถ่ายภาพด้วยตัวเองที่ Java

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