ประเภทใน Lisp และ Scheme


10

ฉันเห็นแล้วว่าแร็กเก็ตมีประเภท เมื่อดูอย่างรวดเร็วครั้งแรกดูเหมือนว่าเกือบจะเหมือนกับการพิมพ์ Haskell แต่ CLOS ของ Lisp ครอบคลุมพื้นที่ Haskell บางประเภทหรือไม่ การสร้างประเภท Haskell ที่เข้มงวดมากและวัตถุในภาษา OO ใด ๆ ดูเหมือนจะคลุมเครือ เป็นเพียงว่าฉันดื่ม Hool-kool-aid บางอย่างและฉันหวาดระแวงโดยสิ้นเชิงว่าถ้าฉันไปตามถนน Lisp ฉันจะเมาเพราะการพิมพ์แบบไดนามิก

คำตอบ:


6

ระบบประเภท CL มีความหมายมากกว่า Haskell เช่นคุณสามารถมี(or (integer 1 10) (integer 20 30))ค่า1,2,...9,10,20,21,...,30ได้

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

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

โดยทั่วไปถ้าคุณต้องการการพิมพ์แบบสแตติกที่แข็งแกร่งให้ใช้ Haskell หรือ OCaml หากคุณต้องการการพิมพ์แบบไดนามิกที่แข็งแกร่งให้ใช้ Lisp ถ้าคุณต้องการพิมพ์แบบสแตติกอ่อนใช้ C ถ้าคุณต้องการพิมพ์แบบไดนามิกอ่อนใช้ Perl / Python แต่ละเส้นทางมีข้อดีของมัน (และ zealots) และข้อเสีย (และผู้ว่า) ดังนั้นคุณจะได้รับประโยชน์จากการเรียนรู้ทั้งหมด


19
ทุกคนที่ใช้คำเช่น "ความปลอดภัยประเภทกำลังลงลำคอของคุณ" ไม่เข้าใจว่าประเภทความปลอดภัยคืออะไรหรือเหตุใดจึงมีประโยชน์
Mason Wheeler

11
@MasonWheeler: ใครก็ตามที่ทำข้อสรุปที่ครอบคลุมจากวลีเดียวจะพบว่าตัวเองผิดบ่อยกว่าที่อื่น เช่นในกรณีนี้
sds

4
เนื่องจากหัวข้อเป็นภาษาคำว่า "คอของคุณ" จึงเหมาะสมและเหมาะสำหรับการใช้ภาพ
luser droog

1
@ KChaloux: ฉันหมายถึง "แสดงออก" ตามที่อธิบายไว้โดยตัวอย่าง
sds

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

5

ประเภทแร็กเก็ตนั้นแตกต่างจาก Haskell มาก พิมพ์ระบบเสียงกระเพื่อมและโครงการและแน่นอนพิมพ์ระบบในระบบนิเวศภาษา untyped ประเพณีโดยทั่วไปมีเป้าหมายพื้นฐานที่ระบบการพิมพ์อื่น ๆ ไม่ได้ - ทำงานร่วมกันด้วยที่มีอยู่รหัส untyped แร็กเก็ตที่พิมพ์แล้วตัวอย่างเช่นแนะนำกฎการพิมพ์ใหม่ทั้งหมดเพื่อรองรับสำนวนแร็กเก็ตต่างๆ พิจารณาฟังก์ชั่นนี้:

(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

สำหรับรายการที่ไม่ว่างนี่จะส่งคืนองค์ประกอบแรก สำหรับรายการที่ว่างเปล่าสิ่งนี้จะคืนค่าเท็จ นี่เป็นเรื่องธรรมดาในภาษาที่ไม่มีการพิมพ์ ภาษาที่พิมพ์จะใช้ wrapper บางชนิดเช่นMaybeหรือโยนข้อผิดพลาดในกรณีที่ว่างเปล่า หากเราต้องการเพิ่มประเภทในฟังก์ชันนี้ควรใช้ประเภทใด มันไม่ใช่[a] -> a(ในรูปแบบ Haskell) เพราะมันสามารถคืนค่าเท็จได้ มันยังไม่ได้[a] -> Either a Booleanเพราะ (1) มันจะคืนค่า false ในกรณีที่ว่างเสมอไม่ใช่บูลีนที่ไม่มีกฎเกณฑ์และ (2) ทั้งสองประเภทจะล้อมรอบองค์ประกอบในLeftและเท็จในRightและต้องการให้คุณ แต่ค่าจะส่งกลับสหภาพที่แท้จริง- ไม่มีตัวสร้างการตัดมันจะส่งคืนหนึ่งประเภทในบางกรณีและอีกประเภทหนึ่งในกรณีอื่น ใน Typed Racket สิ่งนี้แสดงด้วยตัวสร้างประเภทร่วม:

(: first (All (A) (-> (Listof A) (U A #f))))
(define (first some-list)
  (if (empty? some-list)
      #f
      (car some-list)))

ประเภท(U A #f)ระบุฟังก์ชั่นสามารถกลับองค์ประกอบของรายการหรือเท็จโดยไม่ต้องEitherอินสแตนซ์ห่อใด ๆ ตัวตรวจสอบชนิดสามารถอนุมานได้ว่าsome-listเป็นชนิด(Pair A (Listof A))หรือรายการว่างและยิ่งไปกว่านั้นมันหมายถึงว่าในสองสาขาของคำสั่ง if เป็นที่รู้จักกันว่าเป็นกรณีใด ตัวตรวจสอบชนิดรู้ว่าใน(car some-list)นิพจน์รายการต้องมีชนิด(Pair A (Listof A))เนื่องจากเงื่อนไข if ทำให้แน่ใจ สิ่งนี้เรียกว่าการพิมพ์ที่เกิดขึ้นและได้รับการออกแบบมาเพื่อให้ง่ายต่อการเปลี่ยนจากรหัสที่ไม่พิมพ์ไปเป็นรหัสที่พิมพ์

ปัญหาคือการโยกย้าย มีโค้ดแร็กเก็ตที่ไม่มีการพิมพ์ออกมามากมายและ Typed Racket ไม่สามารถบังคับให้คุณละทิ้งไลบรารีที่ไม่ได้พิมพ์ที่คุณชื่นชอบทั้งหมดและใช้เวลาหนึ่งเดือนในการเพิ่มประเภทให้กับโค้ดเบสของคุณหากคุณต้องการใช้ ปัญหานี้จะเกิดขึ้นทุกครั้งที่คุณเพิ่มประเภทไปยัง codebase ที่มีอยู่ค่อยๆดู TypeScript และประเภทใด ๆ สำหรับแอปพลิเคชัน javascript ของแนวคิดเหล่านี้

ระบบประเภททีละน้อยจะต้องมีเครื่องมือสำหรับการจัดการกับสำนวนที่ไม่มีการพิมพ์ทั่วไปและการโต้ตอบกับโค้ดที่ไม่มีการพิมพ์ที่มีอยู่ การใช้มันจะค่อนข้างเจ็บปวดอย่างอื่นดู"ทำไมเราไม่ใช้ Core.typed" อีกต่อไปสำหรับตัวอย่าง Clojure


1
ในทางเศรษฐศาสตร์สิ่งนี้เรียกว่ากฎของ Gresham : เงินที่ไม่ดีขับออกมาดี มันมีแนวโน้มที่จะหาแอปพลิเคชันในด้านวิศวกรรมเช่นกัน เมื่อคุณมีระบบย่อยเทียบเท่าสองทฤษฎีในระบบของคุณบ่อยครั้งที่แย่กว่านั้นจะทำให้เกิดปัญหามากเกินไปที่จะทำให้ระบบที่ดีกว่าใช้คุ้มค่าตามที่เราเห็นอยู่ที่นี่
Mason Wheeler

"ตัวอย่างของการออกแบบระบบชนิดที่": ประโยคนี้ไม่สมบูรณ์
coredump

@MasonWheeler สิ่งที่แย่กว่านั้นคือให้ฉันเดาการตรวจสอบประเภทแบบไดนามิก ดังนั้นทันทีที่คุณแนะนำมันไม่คุ้มกับการวิเคราะห์แบบคงที่บ้างไหม?
coredump

1
@coredump - การพิมพ์ทีละส่วนนั้นคุ้มค่าเว้นแต่คุณจะโต้ตอบกับโค้ดที่พิมพ์ออกมาไม่ดี ตัวอย่างประเภทใด ๆ ใน TypeScript เพียงแค่บอกให้ตัวพิมพ์ดีดเลิกโดยทั่วไปจะทิ้งประโยชน์ของระบบประเภทเพราะอาจทำให้ค่าที่ไม่ดีในการเผยแพร่ผ่านรหัสตรวจสอบแบบคงที่จำนวนมาก แร็กเก็ตที่พิมพ์โดยใช้สัญญาและขอบเขตของโมดูลเพื่อหลีกเลี่ยงปัญหานี้
แจ็ค

1
@coredump อ่านบทความ Clojure มีการพิมพ์แบบไดนามิกรอบ ๆ โดยเฉพาะอย่างยิ่งกับรหัสของบุคคลที่สามที่ไม่มีประเภทแบบคงที่ทั้งหมดทำให้สิ่งต่าง ๆ คลาดเคลื่อนสำหรับความพยายามที่จะปรับปรุง codebase ของพวกเขาด้วยประเภทคงที่
Mason Wheeler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.