ใช่คำถามตรงไปตรงมาบนพื้นผิว แต่ถ้าคุณใช้เวลาในการคิดจนจบคุณจะเข้าสู่ความลึกของทฤษฎีประเภทที่ไม่สามารถวัดได้ และทฤษฎีประเภทก็จ้องมองคุณเช่นกัน
ขั้นแรกคุณต้องเข้าใจอย่างถูกต้องแล้วว่า F # ไม่มีคลาสประเภทและนั่นคือสาเหตุ Mappable
แต่คุณเสนออินเตอร์เฟซ ตกลงมาดูกันดีกว่า
สมมติว่าเราสามารถประกาศส่วนต่อประสานดังกล่าวได้ คุณลองจินตนาการดูว่าลายเซ็นของมันจะเป็นอย่างไร?
type Mappable =
abstract member map : ('a -> 'b) -> 'f<'a> -> 'f<'b>
ไหนf
เป็นประเภทการใช้อินเตอร์เฟซ โอ้เดี๋ยวก่อน! F # ไม่มีสิ่งนั้น! นี่f
คือตัวแปรชนิดที่สูงกว่าและ F # ไม่มีความเมตตาที่สูงขึ้นเลย ไม่มีวิธีที่จะประกาศฟังก์ชั่นf : 'm<'a> -> 'm<'b>
หรืออะไรทำนองนั้น
แต่โอเคสมมติว่าเราได้ข้ามอุปสรรค์นั้นด้วย และตอนนี้เรามีอินเตอร์เฟซMappable
ที่สามารถดำเนินการโดยList
, Array
, Seq
และอ่างล้างจาน แต่เดี๋ยวก่อน! ตอนนี้เรามีวิธีการแทนฟังก์ชั่นและวิธีการเขียนไม่ดี! ดูที่การเพิ่ม 42 ให้กับองค์ประกอบทั้งหมดของรายการซ้อนกัน:
// Good ol' functions:
add42 nestedList = nestedList |> List.map (List.map ((+) 42))
// Using an interface:
add42 nestedList = nestedList.map (fun l -> l.map ((+) 42))
ดู: ตอนนี้เราต้องใช้แลมบ์ดาแสดงออก! ไม่มีทางที่จะผ่านสิ่งนี้ได้.map
การใช้งานไปยังฟังก์ชั่นอื่นเป็นค่า จุดจบของ "ฟังก์ชั่นเป็นค่า" อย่างมีประสิทธิภาพ (และใช่ฉันรู้ว่าการใช้แลมบ์ดาไม่ได้ดูแย่มากในตัวอย่างนี้ แต่เชื่อฉันสิมันน่าเกลียดมาก)
แต่เดี๋ยวก่อนเรายังไม่เสร็จ ตอนนี้เป็นการเรียกเมธอดการอนุมานชนิดไม่ทำงาน! เนื่องจากลายเซ็นชนิดของวิธีการ. NET ขึ้นอยู่กับชนิดของวัตถุจึงไม่มีวิธีใดที่คอมไพเลอร์จะอนุมานทั้งคู่ นี่เป็นปัญหาที่พบบ่อยมากสำหรับมือใหม่ที่เข้ามาทำงานร่วมกันกับไลบรารี NET และการรักษาเพียงอย่างเดียวคือการให้ลายเซ็นประเภท:
add42 (nestedList : #Mappable) = nestedList.map (fun l -> l.map ((+) 42))
โอ้ แต่นี่ยังไม่เพียงพอ! ถึงแม้ว่าผมจะได้จัดให้มีลายเซ็นสำหรับตัวเองผมไม่ได้ให้ลายเซ็นสำหรับพารามิเตอร์แลมบ์ดาฯnestedList
l
ลายเซ็นดังกล่าวควรเป็นอย่างไร คุณจะบอกว่ามันควรจะเป็นfun (l: #Mappable) -> ...
อย่างไร โอ้และตอนนี้เราก็ต้องจัดอันดับประเภท -N ตามที่คุณเห็น#Mappable
มันเป็นทางลัดสำหรับ "ประเภทใดก็ได้'a
เช่นนั้น'a :> Mappable
" - นั่นคือการแสดงออกแลมบ์ดาซึ่งเป็นเรื่องทั่วไป
หรืออีกวิธีหนึ่งเราสามารถกลับไปสู่ความใจดีที่สูงขึ้นและประกาศประเภทที่nestedList
แม่นยำยิ่งขึ้น:
add42 (nestedList : 'f<'a<'b>> where 'f :> Mappable, 'a :> Mappable) = ...
แต่ตกลงเอาล่ะอนุมานประเภทสำหรับตอนนี้และกลับไปที่การแสดงออกแลมบ์ดาและวิธีที่เราไม่สามารถผ่านmap
เป็นค่าไปยังฟังก์ชั่นอื่น สมมติว่าเราขยายไวยากรณ์เล็กน้อยเพื่อให้สิ่งที่ Elm ทำกับฟิลด์บันทึก:
add42 nestedList = nestedList.map (.map ((+) 42))
ประเภทของสิ่งที่.map
จะเป็นอย่างไร มันจะต้องเป็นประเภทที่มีข้อ จำกัดเหมือนใน Haskell!
.map : Mappable 'f => ('a -> 'b) -> 'f<'a> -> 'f<'b>
ว้าวโอเค การใส่ความจริงที่. NET ไม่อนุญาตให้มีประเภทดังกล่าวมีอยู่จริงเราเพิ่งกลับมาเรียนประเภท!
แต่มีเหตุผลที่ F # ไม่มีคลาสประเภทในตอนแรก หลายแง่มุมของเหตุผลนั้นได้อธิบายไว้ข้างต้น แต่วิธีที่กระชับกว่าคือ: ความเรียบง่ายความเรียบง่าย
คุณเห็นไหมว่านี่เป็นลูกไหมพรม เมื่อคุณมีคลาสประเภทคุณจะต้องมีข้อ จำกัด ความมีน้ำใจที่สูงขึ้นอันดับ -N (หรืออย่างน้อยอันดับ 2) และก่อนที่คุณจะรู้คุณจะต้องขอประเภทที่ไม่น่าจดจำฟังก์ชันประเภท GADT และทั้งหมด ส่วนที่เหลือของมัน
แต่ Haskell จ่ายราคาให้กับสินค้าทุกอย่าง ปรากฎว่าไม่มีวิธีที่ดีในการอนุมานทุกสิ่งนั้น ประเภทที่เรียงลำดับสูงกว่าทำงานได้ แต่ข้อ จำกัด ไม่ได้เป็นเช่นนั้นแล้ว อันดับ -N - อย่าแม้แต่จะฝันถึงมัน และแม้ว่าจะได้ผลก็ตามคุณจะได้รับข้อผิดพลาดประเภทที่คุณต้องมีปริญญาเอกเพื่อให้เข้าใจ และนั่นคือสาเหตุที่ Haskell คุณได้รับการสนับสนุนอย่างอ่อนโยนให้ใส่ลายเซ็นพิมพ์ลงบนทุกสิ่ง ไม่ใช่ทุกอย่างทุกอย่างแต่จริง ๆ แล้วเกือบทุกอย่าง และที่คุณไม่ได้ใส่ลายเซ็นประเภท (เช่นภายในlet
และwhere
) - เซอร์ไพร์สเซอร์ไพร์สสถานที่เหล่านั้นถูกทำให้เป็นโมฆะจริง ๆ
ใน F # ในทางกลับกันลายเซ็นของพิมพ์นั้นหายากส่วนใหญ่จะใช้สำหรับเอกสารหรือสำหรับ. NET interop นอกเหนือจากสองกรณีนี้คุณสามารถเขียนโปรแกรมขนาดใหญ่ที่ซับซ้อนทั้งหมดใน F # และไม่ใช้ลายเซ็นประเภทหนึ่งครั้ง การอนุมานประเภททำงานได้ดีเพราะไม่มีอะไรซับซ้อนเกินไปหรือคลุมเครือให้จัดการ
และนี่คือข้อได้เปรียบที่ยิ่งใหญ่ของ F # มากกว่า Haskell ใช่ Haskell ช่วยให้คุณแสดงสิ่งที่ซับซ้อนเป็นพิเศษอย่างแม่นยำนั่นเป็นเรื่องดี แต่ F # ช่วยให้คุณล้างแค้นได้ดีเกือบเหมือน Python หรือ Ruby และยังมีคอมไพเลอร์ที่คอยจับคุณถ้าคุณสะดุด