แนวคิดขั้นสูงของ Haskell มีความสำคัญอย่างไรเช่น Monads และ Applicative Functors สำหรับงานเขียนโปรแกรมตามปกติ


16

ฉันได้อ่าน Learn You a Haskell เล่มถึงจุดที่พวกเขาแนะนำ Monads และสิ่งต่างๆเช่น Just a สิ่งเหล่านี้มีประโยชน์สำหรับฉันมากจนฉันรู้สึกอยากยอมแพ้พยายามเรียนรู้มัน

แต่ฉันอยากลองจริงๆ

ฉันสงสัยว่าอย่างน้อยฉันสามารถหลีกเลี่ยงแนวคิดขั้นสูงได้สักพักและเริ่มใช้ส่วนอื่น ๆ ของภาษาเพื่อทำงานที่มีประโยชน์มากมายด้วยส่วนพื้นฐานของ Haskell เช่นฟังก์ชั่นมาตรฐาน IO การจัดการไฟล์ การเชื่อมต่อฐานข้อมูลและไม่ชอบ

นี่เป็นวิธีการที่สมเหตุสมผลในการเรียนรู้วิธีใช้ Haskell หรือไม่?


2
ฉันไม่รู้ว่าคุณจะไปได้ไกลแค่ไหน หากไม่มี monads คุณจะไม่สามารถทำอะไรที่มีประโยชน์ใน Haskell ได้เนื่องจากการทำสิ่งที่มีประโยชน์ในการเขียนโปรแกรม (เช่นการเขียนโปรแกรมที่จริงจังซึ่งมีคนต้องการใช้) ต้องใช้ I / O และสถานะซึ่งทั้งสองสามารถจัดการได้ใน Haskell เท่านั้น ผ่านการใช้ monads
Mason Wheeler

1
@dan - ในที่สุดฉันก็เข้าใจพระ Haskell จากการอ่านเอกสารสองฉบับ - "คุณน่าจะประดิษฐ์ monads" และ "Tackling the Awkward Squad" ฉันไม่สามารถพูดได้ว่าพวกเขาดีกว่า "Learn You a Haskell" ซึ่งฉันไม่ได้อ่าน แต่พวกเขาก็ทำงานให้ฉัน ในการใช้สัญลักษณ์ที่ทำกับ IO monad คุณสามารถเข้าใจสิ่งที่ monads ได้ - คุณจะทำบางสิ่งที่จะทำให้ผู้เชี่ยวชาญหัวเราะหรือร้องไห้ แต่อย่างน้อยก็ไม่ต่างจากการใช้ ภาษาที่จำเป็นธรรมดา แต่มันก็คุ้มค่าที่จะได้รับความเข้าใจที่ลึกซึ้งยิ่งขึ้น
Steve314

2
@ ด่านฉันใช้เวลาเกือบทศวรรษในการเบื่อ OOP และเริ่มชื่นชม / อยากรู้อยากเห็นเกี่ยวกับภาษาที่ใช้งานได้ ฉันยังคงเรียนรู้ F # และ Clojure ก่อนที่ฉันจะลอง Haskell อย่างจริงจัง ในขณะที่ความท้าทายส่วนบุคคลเป็นสิ่งที่ดีมีบางสิ่งที่จะพูดเกี่ยวกับความรู้สึกของคุณและเส้นทางของการต่อต้านน้อยที่สุด มากขึ้นอยู่กับว่าคุณเป็นใคร หากคุณเป็นคนที่มีความเฉียบแหลมมากคุณอาจจะชอบ Haskell / Schem ตั้งแต่แรก หากคุณเป็นคนที่ 'ทำเสร็จ' มากกว่าก็โอเคเช่นกัน อย่าเข้าใกล้ Haskell ด้วยทัศนคติ 'ทำหรือตาย' ไปเรียนรู้สิ่งอื่น ๆ และเมื่อเบื่อกลับมา
งาน

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

@ ด่านตลกฉันพบว่าไวยากรณ์ของ Clojure ค่อนข้างเป็นมิตร แต่นั่นอาจเป็นเพราะฉันเคยได้รับ Scheme มาก่อน บางทีหลังจาก F # ฉันจะชินกับวิธีที่ Haskell ทำสิ่งต่าง ๆ
งาน

คำตอบ:


16

ก่อนอื่นเรามาแยกความแตกต่างระหว่างการเรียนรู้แนวคิดนามธรรมและการเรียนรู้ตัวอย่างเฉพาะของพวกเขา

คุณจะไม่ไปไกลเกินกว่าจะเพิกเฉยกับตัวอย่างที่เฉพาะเจาะจงทั้งหมดด้วยเหตุผลง่ายๆว่าพวกมันแพร่หลายมากที่สุด ในความเป็นจริง abstractions มีอยู่เป็นส่วนใหญ่เพราะพวกเขารวมสิ่งที่คุณจะทำต่อไปด้วยตัวอย่างเฉพาะ

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

ใช้Maybe aเป็นตัวอย่าง มันเป็นเพียงประเภทข้อมูล:

data Maybe a = Just a | Nothing

มันคือทั้งหมด แต่การจัดทำเอกสารด้วยตนเอง; มันเป็นค่าเผื่อเลือก ไม่ว่าคุณจะมีอะไรบางอย่าง "แค่" aหรือคุณไม่มีอะไรเลย สมมติว่าคุณมีฟังก์ชั่นการค้นหาบางประเภทที่กลับMaybe Stringไปเป็นตัวแทนการค้นหาStringค่าที่อาจไม่มีอยู่ ดังนั้นรูปแบบที่คุณจับคู่กับค่าเพื่อดูว่ามันคืออะไร:

case lookupFunc key of
    Just val -> ...
    Nothing  -> ...

นั่นคือทั้งหมด!

จริงๆไม่มีอะไรที่คุณต้องการ ไม่มีFunctors หรือMonads หรือสิ่งอื่นใด สิ่งเหล่านั้นแสดงให้เห็นถึงวิธีการทั่วไปในการใช้Maybe aค่า ... แต่เป็นเพียงสำนวน "รูปแบบการออกแบบ" สิ่งที่คุณอาจต้องการเรียก

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

ในความเป็นจริงนี่เป็นแผ่นโกงสำหรับสิ่งที่คุณจริงๆต้องรู้เกี่ยวกับIOตอนนี้:

  • หากสิ่งที่มีประเภทIO aนั่นหมายความว่ามันเป็นขั้นตอนที่ทำอะไรบางอย่างและคายaค่า

  • เมื่อคุณมีบล็อกของรหัสโดยใช้doสัญลักษณ์เขียนสิ่งนี้:

    do -- ...
       inp <- getLine
       -- etc...
    

    ... หมายถึงดำเนินการตามขั้นตอนทางด้านขวาของ<-และกำหนดผลลัพธ์ให้กับชื่อทางด้านซ้าย

  • โดยที่ถ้าคุณมีสิ่งนี้:

    do -- ...
       let x = [foo, bar]
       -- etc...
    

    ... หมายถึงการกำหนดค่าของนิพจน์ธรรมดา (ไม่ใช่โพรซีเดอร์) ทางด้านขวาของ=ชื่อให้ทางด้านซ้าย

  • หากคุณใส่บางอย่างที่นั่นโดยไม่กำหนดค่าเช่นนี้

    do putStrLn "blah blah, fishcakes"
    

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

  • การดำเนินการขั้นตอนเดียวกันมากกว่าหนึ่งครั้งสามารถให้ผลลัพธ์ที่แตกต่างกัน นั่นเป็นแนวคิด นี่คือสาเหตุที่ไม่มีวิธี "ลบ" ออกIOจากค่าเนื่องจากบางสิ่งในIOไม่ใช่ค่ามันเป็นขั้นตอนในการรับค่า

  • บรรทัดสุดท้ายในdoบล็อกต้องเป็นกระบวนการธรรมดาที่ไม่มีการกำหนดโดยที่ค่าส่งคืนของกระบวนงานนั้นจะกลายเป็นค่าส่งคืนสำหรับบล็อกทั้งหมด หากคุณต้องการให้ค่าส่งคืนใช้ค่าบางค่าที่กำหนดไว้แล้วreturnฟังก์ชั่นจะใช้ค่าธรรมดาและให้ขั้นตอนการไม่ใช้งานที่คืนค่านั้น

  • นอกเหนือจากนั้นไม่มีอะไรพิเศษเกี่ยวกับIO; ขั้นตอนเหล่านี้เป็นค่านิยมธรรมดา ๆ และคุณสามารถส่งต่อและรวมเข้าด้วยกันในรูปแบบที่ต่าง มันก็ต่อเมื่อพวกเขาถูกdoบล็อกในบางกรณีmainที่พวกเขาทำอะไร

ดังนั้นในบางสิ่งเช่นโปรแกรมตัวอย่างสำเร็จรูปที่น่าเบื่ออย่างที่สุดนี้:

hello = do putStrLn "What's your name?"
           name <- getLine
           let msg = "Hi, " ++ name ++ "!"
           putStrLn msg
           return name

... คุณสามารถอ่านได้เหมือนโปรแกรมที่จำเป็น helloเรากำลังกำหนดขั้นตอนที่มีชื่อว่า เมื่อดำเนินการขั้นแรกให้ดำเนินการตามขั้นตอนเพื่อพิมพ์ข้อความเพื่อขอชื่อของคุณ ถัดไปจะประมวลผลโพรซีเดอร์ที่อ่านบรรทัดอินพุตและกำหนดผลลัพธ์ให้name; แล้วก็จะระบุการแสดงออกชื่อmsg; จากนั้นจะพิมพ์ข้อความ จากนั้นจะส่งคืนชื่อผู้ใช้เป็นผลลัพธ์ของบล็อกทั้งหมด เนื่องจากnameเป็นStringวิธีที่helloเป็นกระบวนการที่ผลตอบแทนจึงมีประเภทString IO Stringและตอนนี้คุณสามารถดำเนินการตามขั้นตอนนี้ที่อื่น ๆ getLineเช่นเดียวกับมันรัน

Pfff, monads ใครต้องการ 'em?


2
@dan: ยินดีต้อนรับคุณมาก! หากคุณมีคำถามเฉพาะระหว่างทางอย่าลังเลที่จะถามมากกว่าเกี่ยวกับ Stack Overflow แท็ก [haskell] นั้นมักจะใช้งานได้ค่อนข้างดีและถ้าคุณอธิบายว่าคุณมาจากคนอื่นจะช่วยให้คุณได้รับความเข้าใจมากกว่าแค่การตอบคำถามที่คลุมเครือ
CA McCann

9

แนวคิดขั้นสูงของ Haskell มีความสำคัญอย่างไรเช่น Monads และ Applicative Functors สำหรับงานเขียนโปรแกรมตามปกติ

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

Monads, Monad Transforms, Monoids, Functors, Funrants ที่ใช้งานได้, Funrav ที่แยกออกจากกัน, Arrows, หมวดหมู่, ฯลฯ เป็นรูปแบบการออกแบบ เมื่อคุณทำสิ่งที่เฉพาะเจาะจงกับสิ่งที่คุณทำให้เป็นหนึ่งในนั้นแล้วคุณจะสามารถดึงฟังก์ชั่นมากมายที่เขียนขึ้นอย่างเป็นนามธรรม คลาสประเภทเหล่านี้ไม่เพียง แต่จะทำให้คุณสับสน แต่ยังมีไว้เพื่อให้ชีวิตของคุณง่ายขึ้นและทำให้คุณมีประสิทธิภาพมากขึ้น ในความคิดของฉันเหตุผลที่ดีที่สุดสำหรับความรัดกุมของรหัส Haskell ที่ดี


1
+1: ขอบคุณสำหรับการชี้ให้เห็นว่า "Monads, Monad Transforms, Monoids, Functors, Funrants ที่ใช้งานได้, Funrav ที่แยกออกจากกัน, ลูกศร, หมวดหมู่, ฯลฯ เป็นรูปแบบการออกแบบ" !
จอร์โจ

7

จำเป็นหรือไม่ถ้าคุณต้องการให้บางสิ่งบางอย่างทำงาน? เลขที่

จำเป็นหรือไม่หากคุณต้องการเขียนโปรแกรมสำนวน Haskell ที่สวยงาม? อย่างแน่นอน

เมื่อการเขียนโปรแกรมใน Haskell ช่วยให้คำนึงถึงสองสิ่ง:

  1. ระบบประเภทมีไว้เพื่อช่วยคุณ ถ้าคุณเขียนโปรแกรมของคุณออกจากรหัส typeclass หนัก polymorphic สูงมากบ่อยครั้งที่คุณจะได้รับในสถานการณ์ที่ยอดเยี่ยมที่ชนิดของฟังก์ชั่นที่คุณต้องการจะนำคุณไปยัง (หนึ่ง! เดี่ยว) ปฏิบัติที่ถูกต้อง

  2. ห้องสมุดที่มีอยู่หลากหลายได้รับการพัฒนาโดยใช้หลักการ # 1

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

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

โชคดี!


1
ขอขอบคุณ! ฉันลังเลระหว่าง Clojure และ Haskell แต่ตอนนี้ฉันกำลังโน้มตัวไปหา Haskell เพราะคนที่เป็นประโยชน์เช่นคุณเป็นอย่างไรบ้าง
แดน

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