ทำไมไม่พิมพ์อย่างพึ่งพา?


161

ฉันได้เห็นแหล่งข้อมูลหลายแห่งสะท้อนความคิดเห็นที่ว่า "Haskell ค่อยๆกลายเป็นภาษาที่ต้องพึ่งพา" ความหมายดูเหมือนว่าจะมีส่วนขยายภาษาเพิ่มมากขึ้น Haskell ลอยไปในทิศทางทั่วไปนั้น แต่ยังไม่มี

โดยทั่วไปมีสองสิ่งที่ฉันอยากรู้ อย่างแรกคือการที่ค่อนข้างง่าย "การเป็นภาษาที่ต้องพึ่งพา" หมายถึงอะไร ? (หวังว่าจะไม่มีความรู้ด้านเทคนิคมากเกินไป)

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

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


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

27
@GManNickG: การตรวจสอบประเภทเป็นไปได้ทั้งหมด การอนุมานประเภทเป็นอีกเรื่องหนึ่ง แต่หลังจากนั้นส่วนขยายต่าง ๆ ของ GHC ก็มีความยาวตั้งแต่ทิ้งความคิดที่ว่ามันควรจะเป็นไปได้ที่จะอนุมานทุกประเภท
CA McCann

7
หากฉันเข้าใจอย่างถูกต้องข้อเสียคือการพิมพ์ที่ถูกต้อง (เช่นในทางที่สามารถใช้งานได้และเป็นที่ยอมรับได้ดี) นั้นเป็นเรื่องยากและเราก็ยังไม่รู้ว่าจะเป็นอย่างไร
comingstorm

1
@CAMcCann: ใช่ความผิดพลาดของฉัน
GManNickG

4
ฉันไม่คิดว่าจะมีใครชี้ให้เห็นถึงข้อเสียเปรียบที่ยิ่งใหญ่อย่างหนึ่ง: การเขียนหลักฐานที่แสดงว่ารหัสทั้งหมดของคุณถูกต้องนั้นค่อนข้างน่าเบื่ออย่างน่าเบื่อ เนื่องจากคุณไม่สามารถทำการอนุมานแบบอัตโนมัติ (สอดคล้องกับทฤษฎีบทที่พิสูจน์ในตรรกะ "เฮลลาที่ทรงพลัง") คุณต้องเขียนคำอธิบายประกอบสำหรับโปรแกรมของคุณในรูปแบบของการพิสูจน์ เห็นได้ชัดว่ามันน่ารำคาญและยากที่จะทำหลังจากผ่านไประยะหนึ่งโดยเฉพาะอย่างยิ่งสำหรับเวทมนตร์ monadic ที่ซับซ้อนกว่าที่คนทั่วไปทำใน Haskell สิ่งที่ใกล้เคียงที่สุดที่เรากำลังมาในวันนี้คือภาษาที่ทำสิ่งนี้ให้เรามากที่สุดหรือให้พื้นฐานที่ดีแก่เรา
Kristopher Micinski

คำตอบ:


21

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

แก้ไข: เห็นได้ชัดว่าจากจุดนี้ไปข้างหน้าฉันผิด (ดูความคิดเห็น @ pigworker ของ) ฉันจะเก็บส่วนที่เหลือไว้นี้เป็นบันทึกของตำนานที่ฉันได้รับอาหาร : P


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

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


46
สิ่งที่คุณพูดเป็นเท็จเกือบทั้งหมด ฉันไม่ได้ตำหนิคุณทั้งหมด: มันซ้ำตำนานมาเป็นความจริง ภาษา Idris ของ Edwin Brady ดำเนินการลบประเภท (เนื่องจากไม่มีลักษณะการทำงานในขณะนั้นขึ้นอยู่กับประเภท) และสร้างการเข้ารหัส supercombinator supercombinator ที่ยกมาจากแล็บด้าซึ่งเป็นมาตรฐานที่สร้างรหัสโดยใช้เทคนิค G-machine ในสต็อค
หมูทำงาน

3
ในฐานะที่เป็นหมายเหตุด้านใครบางคนเพิ่งชี้ให้ฉันกระดาษนี้ จากสิ่งที่ฉันสามารถบอกได้มันจะทำให้ Haskell เป็นคนที่พึ่งพาได้ (นั่นคือภาษาระดับประเภทจะเป็นคนที่พึ่งพาได้) ซึ่งใกล้เคียงที่สุดเท่าที่ฉันจะเห็นเราได้รับในไม่ช้า
Flame ของ Ptharien

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

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

2
@pigworker Haskell มีเซตย่อยทั้งหมดหรือไม่ ถ้าเป็นเช่นนั้นเราไม่สามารถใช้สิ่งนั้นกับ "ส่วนของภาษาที่มีอยู่ในทั้งค่าและเลเยอร์ชนิด" ได้หรือไม่ ถ้าไม่ทำเช่นนี้จะใช้เวลาสร้างอะไร
Flame ของ Ptharien

223

พิมพ์ Haskell ไม่ได้แล้วตอนนี้?

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

ประเภทข้อมูลที่ง่ายได้รับการเลื่อนระดับเป็นประเภทเพื่อให้ค่าที่มีอยู่สามารถนำมาใช้ในประเภท ดังนั้นตัวอย่างตามแบบฉบับ

data Nat = Z | S Nat

data Vec :: Nat -> * -> * where
  VNil   :: Vec Z x
  VCons  :: x -> Vec n x -> Vec (S n) x

เป็นไปได้และด้วยความหมายเช่น

vApply :: Vec n (s -> t) -> Vec n s -> Vec n t
vApply VNil         VNil         = VNil
vApply (VCons f fs) (VCons s ss) = VCons (f s) (vApply fs ss)

ซึ่งเป็นสิ่งที่ดี โปรดทราบว่ายาวเป็นสิ่งที่คงที่อย่างหมดจดในฟังก์ชั่นที่มั่นใจว่าอินพุตและเอาต์พุตเวกเตอร์มีความยาวเดียวกันแม้ว่าความยาวที่มีบทบาทในการดำเนินการใดnvApplyในทางตรงกันข้ามมันมาก trickier (กล่าวคือเป็นไปไม่ได้) ที่จะใช้ฟังก์ชั่นที่ทำให้nสำเนาที่กำหนดx(ซึ่งจะเป็นpureไปvApply's <*>)

vReplicate :: x -> Vec n x

เพราะมันสำคัญที่จะต้องรู้ว่าจะต้องทำสำเนากี่ชุดในเวลาทำงาน ป้อนซิงเกิลตัน

data Natty :: Nat -> * where
  Zy :: Natty Z
  Sy :: Natty n -> Natty (S n)

สำหรับโปรโมเตอร์ประเภทใด ๆ เราสามารถสร้างตระกูลซิงเกิลตันจัดทำดัชนีเหนือประเภทโปรโมตซึ่งอาศัยอยู่ด้วยค่าซ้ำของรันไทม์ เป็นประเภทของสำเนาเวลาทำงานของประเภทระดับNatty n n :: Natตอนนี้เราสามารถเขียน

vReplicate :: Natty n -> x -> Vec n x
vReplicate Zy     x = VNil
vReplicate (Sy n) x = VCons x (vReplicate n x)

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

น่ารังเกียจคืออะไร สิ่งที่ขาดหายไป?

ลองสร้างแรงกดดันเล็กน้อยเกี่ยวกับเทคโนโลยีนี้และดูว่าอะไรที่เริ่มต้นการโยกเยก เราอาจได้รับความคิดที่ว่าซิงเกิลควรจะจัดการได้ง่ายขึ้นโดยปริยาย

class Nattily (n :: Nat) where
  natty :: Natty n
instance Nattily Z where
  natty = Zy
instance Nattily n => Nattily (S n) where
  natty = Sy natty

ทำให้เราเขียนพูดได้

instance Nattily n => Applicative (Vec n) where
  pure = vReplicate natty
  (<*>) = vApply

ใช้งานได้แล้ว แต่ตอนนี้มันหมายความว่าแบบดั้งเดิมของเราNatได้วางไข่สามชุด: แบบครอบครัวเดี่ยวและชั้นเดี่ยว เรามีกระบวนการที่ค่อนข้างน่าเบื่อสำหรับการแลกเปลี่ยนNatty nค่าและNattily nพจนานุกรมที่ชัดเจน ยิ่งไปกว่านั้นNattyคือNat : เรามีการพึ่งพาบางอย่างเกี่ยวกับค่ารันไทม์ แต่ไม่ใช่ประเภทที่เราคิดถึงเป็นอันดับแรก ไม่มีการพิมพ์ภาษาอย่างเต็มที่ทำให้ประเภทขึ้นอยู่กับความซับซ้อนนี้!

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

data Vec (n :: Nat) (x :: *)
  = n ~ Z => VNil
  | forall m. n ~ S m => VCons x (Vec m x)

Natในแต่ละสมการทั้งสองของเราทั้งสองฝ่ายมีชนิด

ทีนี้ลองใช้คำแปลเดียวกันกับบางสิ่งที่มีการทำดัชนีเหนือเวกเตอร์

data InVec :: x -> Vec n x -> * where
  Here :: InVec z (VCons z zs)
  After :: InVec z ys -> InVec z (VCons y ys)

กลายเป็น

data InVec (a :: x) (as :: Vec n x)
  = forall m z (zs :: Vec x m). (n ~ S m, as ~ VCons z zs) => Here
  | forall m y z (ys :: Vec x m). (n ~ S m, as ~ VCons y ys) => After (InVec z ys)

และตอนนี้เราสร้างข้อ จำกัด ที่เท่าเทียมกันระหว่างas :: Vec n xและ VCons z zs :: Vec (S m) xที่ซึ่งทั้งสองฝ่ายมีชนิดที่แตกต่างกัน แกน GHC ยังไม่พร้อมสำหรับแนวคิดนี้!

อะไรหายไป? ดีHaskell ส่วนใหญ่หายไปจากระดับประเภท ภาษาของคำศัพท์ที่คุณสามารถโปรโมตมีเพียงตัวแปรและตัวสร้างที่ไม่ใช่ GADT จริงๆ เมื่อคุณมีสิ่งเหล่านี้แล้วtype familyเครื่องจักรจะอนุญาตให้คุณเขียนโปรแกรมประเภทระดับ: บางส่วนอาจจะเป็นฟังก์ชั่นที่คุณควรพิจารณาที่จะเขียนในระดับเทอม (เช่นNatมีการเพิ่มนอกจากนี้เพื่อให้คุณสามารถเพิ่มประเภทที่ดีVec) แต่นั่นเป็นเพียงเรื่องบังเอิญ!

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

เรียกใช้โปรแกรมประเภทระดับ

Haskell เช่นเดียวกับภาษาการเขียนโปรแกรมที่พึ่งพาได้ส่วนใหญ่มี ความหมายในการปฏิบัติงานสองประการ มีวิธีที่ระบบรันไทม์รันโปรแกรม (นิพจน์ที่ปิดแล้วเท่านั้นหลังจากการลบประเภทปรับให้เหมาะสมที่สุดแล้ว) และมีวิธีที่ตัวพิมพ์ดีดรันโปรแกรม (ตระกูลประเภทของคุณ "ประเภทคลาส Prolog" ของคุณพร้อมด้วยนิพจน์แบบเปิด) สำหรับ Haskell โดยปกติคุณจะไม่รวมสองโปรแกรมเข้าด้วยกันเพราะโปรแกรมที่ดำเนินการอยู่ในภาษาต่างๆ ภาษาที่พิมพ์ออกมาอย่างไม่น่าเชื่อมีรูปแบบการดำเนินการแยกจากกันและแบบคงที่สำหรับภาษาเดียวกันของโปรแกรม แต่ไม่ต้องกังวลรุ่นของรันไทม์ยังคงช่วยให้คุณทำการลบประเภทและแน่นอนการลบหลักฐาน: นั่นคือสิ่งที่ Coqการสกัดกลไกให้คุณ อย่างน้อยสิ่งที่คอมไพเลอร์ของ Edwin Brady ทำ (แม้ว่า Edwin จะลบค่าที่ทำซ้ำโดยไม่จำเป็นรวมถึงประเภทและการพิสูจน์) ความแตกต่างของเฟสอาจไม่ใช่ความแตกต่างของหมวดหมู่ประโยค อีกต่อไป แต่มันยังมีชีวิตอยู่และดี

ภาษาที่พิมพ์ออกมาอย่างไม่ถูกต้องรวมทั้งหมดให้ตัวพิมพ์ดีดทำงานโปรแกรมโดยปราศจากความกลัวในสิ่งที่เลวร้ายยิ่งกว่าการรอนาน เมื่อ Haskell พิมพ์มากขึ้นเราต้องเผชิญกับคำถามว่ารูปแบบการดำเนินการแบบคงที่ควรเป็นอย่างไร วิธีการหนึ่งอาจเป็นการ จำกัด การใช้งานสแตติกให้กับฟังก์ชั่นทั้งหมดซึ่งจะทำให้เรามีอิสระในการเรียกใช้ แต่อาจบังคับให้เราสร้างความแตกต่าง (อย่างน้อยสำหรับโค้ดระดับประเภท) ระหว่างข้อมูลและ codata เพื่อให้เราสามารถบอกได้ว่า บังคับใช้การเลิกจ้างหรือผลิตภาพ แต่นั่นไม่ใช่วิธีการเดียว เรามีอิสระในการเลือกรูปแบบการดำเนินการที่อ่อนแอกว่าซึ่งไม่เต็มใจที่จะเรียกใช้โปรแกรมในราคาที่ทำให้สมการน้อยลงโดยการคำนวณ และนั่นคือสิ่งที่ GHC ทำ กฎการพิมพ์สำหรับ GHC core ไม่ได้กล่าวถึงทำงาน โปรแกรม แต่เพียงเพื่อตรวจสอบหลักฐานสมการ เมื่อแปลไปที่แกนกลางตัวแก้ไขข้อ จำกัด ของ GHC จะพยายามเรียกใช้โปรแกรมระดับประเภทของคุณสร้างหลักฐานทางสีเงินเล็กน้อยว่านิพจน์ที่ให้นั้นเท่ากับรูปแบบปกติ วิธีการสร้างหลักฐานนี้เป็นสิ่งที่คาดเดาไม่ได้และไม่สมบูรณ์อย่างหลีกเลี่ยงไม่ได้: มันต่อสู้กับการเรียกดูซ้ำ ๆ อย่างน่ากลัวและนั่นอาจฉลาด สิ่งหนึ่งที่เราไม่ต้องกังวลก็คือการประมวลผลการIO คำนวณในเครื่องพิมพ์ดีด: จำไว้ว่าเครื่องพิมพ์ดีดไม่จำเป็นต้องให้ launchMissilesความหมายเดียวกับที่ระบบรันไทม์!

วัฒนธรรม Hindley-Milner

ระบบพิมพ์ Hindley-Milner ประสบความสำเร็จอย่างน่าประหลาดใจในความแตกต่างสี่ประการด้วยผลข้างเคียงทางวัฒนธรรมที่โชคร้ายที่หลายคนไม่สามารถมองเห็นความแตกต่างระหว่างความแตกต่างและถือว่าความบังเอิญเป็นสิ่งที่หลีกเลี่ยงไม่ได้! ฉันกำลังพูดถึงอะไร

  • คำศัพท์เทียบกับประเภท
  • สิ่งที่เขียนอย่างชัดเจนเทียบกับสิ่งที่เขียนโดยนัย
  • การปรากฏตัวในเวลาทำงานเทียบกับการลบออกก่อนเวลาทำงาน
  • นามธรรมไม่ใช่ขึ้นอยู่กับเทียบกับปริมาณขึ้นอยู่กับ

เราคุ้นเคยกับการเขียนเงื่อนไขและประเภทการแยกที่จะอนุมาน ... แล้วจึงลบ เราคุ้นเคยกับการวัดปริมาณของตัวแปรประเภทด้วยสิ่งที่เป็นนามธรรมประเภทและการใช้งานที่เกิดขึ้นอย่างเงียบ ๆ และแบบคงที่

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

เราควรทบทวนตัวเลือกการออกแบบภาษาใดเนื่องจากความบังเอิญเหล่านี้ไม่ได้เกิดขึ้นอีกต่อไป? ยกตัวอย่างเช่นมันถูกต้องforall x. tหรือเปล่าที่ Haskell ไม่มีทางยกตัวอย่างปริมาณที่ชัดเจน? หากเครื่องพิมพ์ดีดไม่สามารถคาดเดาได้xโดย unifiying tเราไม่มีวิธีอื่นที่จะพูดในสิ่งที่xต้องเป็น

ในวงกว้างเราไม่สามารถปฏิบัติต่อ "การอนุมานแบบ" เป็นแนวคิดแบบเสาหินที่เรามีทั้งหมดหรือไม่มีเลย สำหรับการเริ่มต้นเราจำเป็นต้องแยกมุมมอง "generalization" (กฎ "let" ของ Milner) ซึ่งอาศัยการ จำกัด ประเภทที่มีอยู่อย่างมากเพื่อให้แน่ใจว่าเครื่องจักรที่โง่สามารถเดาได้จากด้าน "ความเชี่ยวชาญ" ของ Milner "กฎ) ซึ่งมีประสิทธิภาพเท่ากับตัวแก้ไขข้อ จำกัด ของคุณ เราสามารถคาดหวังได้ว่าประเภทระดับบนสุดจะยากต่อการอนุมาน แต่ข้อมูลประเภทภายในจะยังคงเผยแพร่ได้ค่อนข้างง่าย

ขั้นตอนถัดไปสำหรับ Haskell

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

เราควรทำให้ระบบข้อ จำกัด ปัจจุบันเป็นเรื่องธรรมดาและทำให้ง่ายขึ้นโดยการอนุญาตให้สมการต่างกันa ~ bที่ชนิดของaและ bไม่เหมือนกัน syntactically (แต่สามารถพิสูจน์ได้เท่ากัน) มันเป็นเทคนิคเก่า (ในวิทยานิพนธ์ของฉันศตวรรษที่ผ่านมา) ซึ่งทำให้การพึ่งพาง่ายขึ้นมากที่จะรับมือกับ เราสามารถแสดงข้อ จำกัด ในการแสดงออกใน GADT และทำให้ผ่อนคลายข้อ จำกัด เกี่ยวกับสิ่งที่สามารถส่งเสริม

เราควรขจัดความจำเป็นในการก่อสร้างแบบซิงเกิลด้วยการนำเสนอฟังก์ชั่นแบบพึ่งพา, pi x :: s -> t. ฟังก์ชั่นที่มีประเภทดังกล่าวสามารถนำไปใช้อย่างชัดเจนในการแสดงออกใด ๆ ของชนิดsที่อาศัยอยู่ในจุดตัดของภาษาประเภทและคำศัพท์ (เช่นตัวแปรตัวแปรคอนสตรัคเตอร์ที่มีมากขึ้นมาในภายหลัง) แลมบ์ดาและแอปพลิเคชันที่เกี่ยวข้องจะไม่ถูกลบในเวลาทำงานดังนั้นเราจึงสามารถเขียนได้

vReplicate :: pi n :: Nat -> x -> Vec n x
vReplicate Z     x = VNil
vReplicate (S n) x = VCons x (vReplicate n x)

โดยไม่ต้องแทนที่โดยNat Nattyโดเมนของpiสามารถเป็นชนิดที่สามารถโปรโมตได้ดังนั้นหากสามารถเลื่อน GADT ได้เราสามารถเขียนลำดับตัวบ่งชี้เชิงปริมาณ (หรือ "telescopes" ตามที่ de Briuijn เรียกว่า)

pi n :: Nat -> pi xs :: Vec n x -> ...

เท่าที่เราต้องการ

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

ยากเกินไป?

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

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

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

ทำไมยังต้องกังวลกับ Haskell

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


6
ฉันไม่สนใจสิ่ง DataKinds จริงๆ fmap read getLine >>= \n -> vReplicate n 0ส่วนใหญ่เป็นเพราะฉันต้องการจะทำอะไรเช่นนี้ ตามที่คุณทราบNattyเป็นวิธีออกไปจากนี้ นอกจากนี้ vReplicate ควรแปลไปยังอาร์เรย์หน่วยความจำจริงเช่นnewtype SVector n x = SVector (Data.Vector.Vector x)ในกรณีที่nมีประเภทNat(หรือคล้ายกัน) อาจจะเป็นอีกจุดสาธิตสำหรับ "การพิมพ์ออกมาอย่างพึ่งพา?"
John L

7
คุณสามารถพูดในสิ่งที่คุณมีในใจสำหรับการรักษาที่เหมาะของการเขียนโปรแกรมที่มีผลกระทบ?
สตีเวนชอว์

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

4
@ ช่างฝีมือคุณใช้ "ไม่มีความแตกต่างเฟส" เป็นตำนาน (คนอื่น ๆ ที่ฉันเห็นว่าเป็นตำนาน) แต่คุณยังไม่ได้รื้ออันนี้ในเอกสารและการพูดคุยที่ฉันได้เห็นและในขณะที่อีกคนที่ฉันนับถือบอกว่า "ทฤษฎีประเภทพึ่งพานั้นแตกต่างจากคอมไพเลอร์ทั่วไปเพราะเราไม่สามารถแยกการตรวจสอบประเภทการรวบรวมและเฟสการดำเนินการ " (ดูโพสต์ล่าสุดของ Andrej 'เมื่อวันที่ 8 พ.ย. 2555) จากประสบการณ์ของฉัน "แกล้งมัน" บางครั้งเราก็เบลอความแตกต่างของเฟสแม้ว่าไม่จำเป็นต้องลบมัน คุณขยายได้ไหมถ้าไม่ใช่ที่นี่แล้วที่อื่นในเรื่องนี้?
sclv

4
@sclv งานของฉันไม่ได้ตั้งเป้าไปที่ตำนาน "ไม่มีเฟสแยกแยะ" แต่คนอื่นมี ฉันขอแนะนำให้ปฏิเสธ "ความแตกต่างของเฟสในการรวบรวม Epigram" โดย James McKinna และ Edwin Brady เป็นจุดเริ่มต้นที่ดี แต่ดูงานที่เก่ากว่ามากในโปรแกรมการสกัดใน Coq การประเมินผลแบบเปิดที่ทำโดยตัวตรวจสอบชนิดจะแยกจากการดำเนินการผ่านการแยกไปยัง ML อย่างสมบูรณ์และเป็นที่ชัดเจนว่าการแยกจะแยกประเภทและหลักฐานออกจากกัน
pigworker

20

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

data Some :: (k -> *) -> * where
  Like :: p x -> Some p

fromInt :: Int -> Some Natty
fromInt 0 = Like Zy
fromInt n = case fromInt (n - 1) of
  Like n -> Like (Sy n)

withZeroes :: (forall n. Vec n Int -> IO a) -> IO a
withZeroes k = do
  Like n <- fmap (fromInt . read) getLine
  k (vReplicate n 0)

*Main> withZeroes print
5
VCons 0 (VCons 0 (VCons 0 (VCons 0 (VCons 0 VNil))))

แก้ไข: หืมที่ควรจะเป็นความเห็นต่อคำตอบของคนงาน ฉันล้มเหลวอย่างชัดเจนดังนั้น


ประโยคแรกของคุณดูแปลกไปหน่อย ฉันจะบอกว่าจุดที่ขึ้นอยู่กับประเภทคือพวกเขาจะทำงานเมื่อข้อมูลมีให้เฉพาะในเวลาทำงาน อย่างไรก็ตามเทคนิคแบบ CPS นี้ไม่เหมือนกัน Vec Zy -> IO Stringสมมติว่าคุณมีฟังก์ชั่น คุณไม่สามารถใช้งานได้withZeroesเนื่องจากZyไม่สามารถรวมประเภทด้วย forall n บางทีคุณสามารถหลีกเลี่ยงปัญหานั้นสำหรับกรณีพิเศษหนึ่งหรือสองกรณี
John L

กุญแจสำคัญเมื่อรับค่าที่พิมพ์ได้ง่าย (เช่น String จาก getLine) และเปลี่ยนเป็นสิ่งที่มีประเภทที่แข็งแกร่งกว่า (เช่น Natty n ด้านบน) คือคุณต้องโน้มน้าวให้ตัวตรวจสอบชนิดที่คุณกำลังทำการตรวจสอบแบบไดนามิกที่จำเป็น ในตัวอย่างของคุณกำลังอ่านจำนวนข้อเพื่อforall nทำให้ความรู้สึก ข้อ จำกัด ที่แม่นยำยิ่งขึ้นสามารถดำเนินการในลักษณะเดียวกัน คุณมีตัวอย่างที่ดีกว่าVec Zy(โปรแกรมยังคงต้องจัดการกับผู้ใช้ที่ป้อนข้อมูล 5 มากกว่า 0)?
ulfnorell

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

1
ulfnorell: ขออภัยฉันยังไม่ชัดเจน สมมติว่าคุณมีฟังก์ชั่นหนึ่งที่จะทำงานด้วยVec Zy -> IO Stringและอีกฟังก์ชั่นสำหรับVec n -> IO Stringและคุณต้องการใช้ฟังก์ชันแรกเฉพาะเมื่อประเภทที่ตรงกัน ใช่มันเป็นไปได้ แต่กลไกสำหรับการเปิดใช้งานมันเป็นเรื่องที่ไม่แน่นอน และนี่คือตรรกะที่ง่ายมาก ถ้าคุณมีตรรกะที่ซับซ้อนมากกว่านี้ นอกจากนี้คุณอาจต้องเขียนโค้ดจำนวนมากใน CPS อีกครั้ง และคุณยังไม่มีการแสดงออกระดับประเภทที่ขึ้นอยู่กับคำที่ระดับค่า
John L

อาฉันเห็นสิ่งที่คุณพูด นี่คือสิ่งที่ Natty มีไว้สำหรับเช่นใน vReplicate ที่เราทำสิ่งต่าง ๆ ขึ้นอยู่กับ n อันที่จริงเรื่องนี้สามารถรับ clunky เล็กน้อย เป็นทางเลือกให้เข้ากับสไตล์ของ CPS คือการทำงานร่วมกับบรรจุขึ้น zeroes :: IO (Some (Flip Vec Int))existentials:
ulfnorell

19

pigworker ให้การอภิปรายที่ยอดเยี่ยมว่าทำไมเราควรมุ่งหน้าไปยังประเภทที่ขึ้นกับ: (a) พวกมันยอดเยี่ยม (b) พวกเขาจะลดความซับซ้อนของสิ่งที่ Haskell ทำไว้แล้ว

สำหรับ "ทำไมไม่" คำถามมีสองประเด็นที่ฉันคิดว่า จุดแรกคือในขณะที่ความคิดพื้นฐานที่อยู่เบื้องหลังประเภทขึ้นอยู่กับเป็นเรื่องง่าย (อนุญาตให้ประเภทขึ้นอยู่กับค่า) การแตกสาขาของความคิดพื้นฐานนั้นมีทั้งความละเอียดอ่อนและลึกซึ้ง ตัวอย่างเช่นความแตกต่างระหว่างค่าและประเภทยังมีชีวิตอยู่และดี; แต่พูดคุยถึงความแตกต่างระหว่างพวกเขากลายเป็นห่างไกลมีความเหมาะสมยิ่งกว่าใน Hinder - Milner หรือ System F ในระดับหนึ่งนี่เป็นเพราะความจริงที่ว่าชนิดที่ขึ้นต่อกันนั้นเป็นพื้นฐานที่ยาก (เช่นตรรกะลำดับแรกไม่สามารถบอกได้) แต่ฉันคิดว่าปัญหาที่ใหญ่กว่านั้นคือเราไม่มีคำศัพท์ที่ดีสำหรับการจับภาพและอธิบายสิ่งที่เกิดขึ้น ในขณะที่ผู้คนจำนวนมากเรียนรู้เกี่ยวกับประเภทที่ขึ้นต่อกันเราจะพัฒนาคำศัพท์ที่ดีขึ้นและดังนั้นสิ่งต่างๆจะกลายเป็นที่เข้าใจง่ายขึ้นแม้ว่าปัญหาพื้นฐานยังคงยากอยู่

จุดที่สองเกี่ยวข้องกับข้อเท็จจริงที่ว่า Haskell คือ กำลังเติบโตที่มีต่อประเภทขึ้นอยู่กับ เนื่องจากเรากำลังคืบหน้าไปยังเป้าหมายนั้นเพิ่มขึ้น แต่หากไม่มีที่นั่นจริงเราก็ติดอยู่กับภาษาที่มีแผ่นแปะเพิ่มตามส่วนบนของแผ่นปะเพิ่ม สิ่งเดียวกันนี้เกิดขึ้นในภาษาอื่นเมื่อความคิดใหม่ ๆ ได้รับความนิยม Java ไม่ได้ใช้เพื่อให้มีความแตกต่าง (ตัวแปร); และเมื่อพวกเขาเพิ่มมันในที่สุดก็เห็นได้ชัดว่าการปรับปรุงที่เพิ่มขึ้นด้วยการรั่วไหลของสิ่งที่เป็นนามธรรมและพลังง่อย ปรากฎว่าการผสม subtyping และ polymorphism นั้นยาก แต่นั่นไม่ใช่เหตุผลที่ Java Generics ทำงานในแบบที่พวกเขาทำ พวกเขาทำงานในแบบที่พวกเขาทำเนื่องจากมีข้อ จำกัด ในการปรับปรุงเพิ่มเติมในรุ่นเก่าของ Java เช่นเดียวกันสำหรับการย้อนกลับไปในวันที่ OOP ถูกประดิษฐ์ขึ้นและผู้คนเริ่มเขียน "วัตถุประสงค์" C (เพื่อไม่ให้สับสนกับ Objective-C) เป็นต้นโปรดจำไว้ว่า C ++ เริ่มต้นภายใต้หน้ากากของการเป็น superset ที่เข้มงวดของ C. การเพิ่มกระบวนทัศน์ใหม่มักจะต้องมีการกำหนดภาษาใหม่หรืออื่น ๆ ที่ลงท้ายด้วยความซับซ้อนบางอย่าง จุดของฉันในเรื่องทั้งหมดนี้คือการเพิ่มประเภทการพึ่งพาจริงให้กับ Haskell จะต้องใช้จำนวนที่แน่นอนของการคว้านและปรับโครงสร้างภาษา - ถ้าเราจะทำถูกต้อง แต่มันก็ยากที่จะยอมรับการยกเครื่องแบบนั้นในขณะที่ความก้าวหน้าที่เพิ่มขึ้นที่เราทำไปดูเหมือนจะถูกลงในระยะสั้น จริงๆแล้วมีไม่กี่คนที่แฮ็ค GHC แต่มีรหัสมรดกจำนวนมากที่ยังมีชีวิตอยู่ นี่เป็นส่วนหนึ่งของสาเหตุที่มีภาษา spinoff มากมายเช่น DDC, Cayenne, Idris เป็นต้น C ++ เริ่มต้นภายใต้หน้ากากของการเป็น superset ที่เข้มงวดของ C. การเพิ่มกระบวนทัศน์ใหม่มักจะต้องมีการกำหนดภาษาใหม่หรืออื่น ๆ ที่ลงท้ายด้วยระเบียบที่ซับซ้อนบางอย่าง จุดของฉันในเรื่องทั้งหมดนี้คือการเพิ่มประเภทการพึ่งพาจริงให้กับ Haskell จะต้องใช้จำนวนที่แน่นอนของการคว้านและปรับโครงสร้างภาษา - ถ้าเราจะทำถูกต้อง แต่มันก็ยากที่จะยอมรับการยกเครื่องแบบนั้นในขณะที่ความก้าวหน้าที่เพิ่มขึ้นที่เราทำไปดูเหมือนจะถูกลงในระยะสั้น จริงๆแล้วมีไม่กี่คนที่แฮ็ค GHC แต่มีรหัสมรดกจำนวนมากที่ยังมีชีวิตอยู่ นี่เป็นส่วนหนึ่งของสาเหตุที่มีภาษา spinoff มากมายเช่น DDC, Cayenne, Idris เป็นต้น C ++ เริ่มต้นภายใต้หน้ากากของการเป็น superset ที่เข้มงวดของ C. การเพิ่มกระบวนทัศน์ใหม่มักจะต้องมีการกำหนดภาษาใหม่หรืออื่น ๆ ที่ลงท้ายด้วยระเบียบที่ซับซ้อนบางอย่าง จุดของฉันในเรื่องทั้งหมดนี้คือการเพิ่มประเภทการพึ่งพาจริงให้กับ Haskell จะต้องใช้จำนวนที่แน่นอนของการคว้านและปรับโครงสร้างภาษา - ถ้าเราจะทำถูกต้อง แต่มันก็ยากที่จะยอมรับการยกเครื่องแบบนั้นในขณะที่ความก้าวหน้าที่เพิ่มขึ้นที่เราทำไปดูเหมือนจะถูกลงในระยะสั้น จริงๆแล้วมีไม่กี่คนที่แฮ็ค GHC แต่มีรหัสมรดกจำนวนมากที่ยังมีชีวิตอยู่ นี่เป็นส่วนหนึ่งของสาเหตุที่มีภาษา spinoff มากมายเช่น DDC, Cayenne, Idris เป็นต้น หรืออื่น ๆ จบลงด้วยความยุ่งเหยิงที่ซับซ้อนบางอย่าง จุดของฉันในเรื่องทั้งหมดนี้คือการเพิ่มประเภทการพึ่งพาจริงให้กับ Haskell จะต้องใช้จำนวนที่แน่นอนของการคว้านและปรับโครงสร้างภาษา - ถ้าเราจะทำถูกต้อง แต่มันก็ยากที่จะยอมรับการยกเครื่องแบบนั้นในขณะที่ความก้าวหน้าที่เพิ่มขึ้นที่เราทำไปดูเหมือนจะถูกลงในระยะสั้น จริงๆแล้วมีไม่กี่คนที่แฮ็ค GHC แต่มีรหัสมรดกจำนวนมากที่ยังมีชีวิตอยู่ นี่เป็นส่วนหนึ่งของสาเหตุที่มีภาษา spinoff มากมายเช่น DDC, Cayenne, Idris เป็นต้น หรืออื่น ๆ จบลงด้วยความยุ่งเหยิงที่ซับซ้อนบางอย่าง จุดของฉันในเรื่องทั้งหมดนี้คือการเพิ่มประเภทการพึ่งพาจริงให้กับ Haskell จะต้องใช้จำนวนที่แน่นอนของการคว้านและปรับโครงสร้างภาษา - ถ้าเราจะทำถูกต้อง แต่มันก็ยากที่จะยอมรับการยกเครื่องแบบนั้นในขณะที่ความก้าวหน้าที่เพิ่มขึ้นที่เราทำไปนั้นดูเหมือนจะถูกลงในระยะสั้น จริงๆแล้วมีไม่กี่คนที่แฮ็ค GHC แต่มีรหัสมรดกจำนวนมากที่ยังมีชีวิตอยู่ นี่เป็นส่วนหนึ่งของสาเหตุที่มีภาษา spinoff มากมายเช่น DDC, Cayenne, Idris เป็นต้น ยากที่จะยอมรับการยกเครื่องแบบนั้นในขณะที่ความก้าวหน้าที่เพิ่มขึ้นที่เราทำไปดูเหมือนจะถูกลงในระยะสั้น จริงๆแล้วมีไม่กี่คนที่แฮ็ค GHC แต่มีรหัสมรดกจำนวนมากที่ยังมีชีวิตอยู่ นี่เป็นส่วนหนึ่งของสาเหตุที่มีภาษา spinoff มากมายเช่น DDC, Cayenne, Idris เป็นต้น ยากที่จะยอมรับการยกเครื่องแบบนั้นในขณะที่ความก้าวหน้าที่เพิ่มขึ้นที่เราทำไปดูเหมือนจะถูกลงในระยะสั้น จริงๆแล้วมีไม่กี่คนที่แฮ็ค GHC แต่มีรหัสมรดกจำนวนมากที่จะมีชีวิตอยู่ นี่เป็นส่วนหนึ่งของสาเหตุที่มีภาษา spinoff มากมายเช่น DDC, Cayenne, Idris เป็นต้น

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