positivity ที่เข้มงวด


10

จากการอ้างอิงนี้: positivity ที่เข้มงวด

เงื่อนไข positivity ที่เข้มงวดออกกฎการประกาศเช่น

data Bad : Set where
 bad : (Bad → Bad) → Bad
         A      B       C
 -- A is in a negative position, B and C are OK

ทำไม A ถึงเป็นลบ ยังอนุญาตให้ใช้ B ทำไม ฉันเข้าใจว่าทำไมอนุญาตให้ C


1
ฉันไม่แน่ใจว่าทำไมสิ่งนี้เรียกว่า "ลบ" แต่เป็นที่รู้กันโดยทั่วไปว่าเกิดข้อผิดพลาด: stack overflow :) รหัสนี้อาจทำให้เกิดการขยายที่ไม่สิ้นสุดAและระเบิดสแต็กในที่สุด
wvxvw

ส่วนนั้นฉันเข้าใจว่าคุณสามารถเขียนสิ่งต่าง ๆ โดยพลการและการคำนวณจะไม่สิ้นสุด ขอบคุณ
Pushpa

1
ฉันคิดว่ามันจะเป็นสิ่งที่ดีที่จะพูดถึงไม่สิ้นสุดในเนื้อหาของคำถามของคุณ ฉันได้อัปเดตคำตอบตามความคิดเห็นของคุณแล้ว
Anton Trunov

@wvxvw ไม่จำเป็นว่ามันอาจจะทำงานตลอดไปโดยไม่ทำให้สแต็คหมดไปหากคอมไพเลอร์ใช้การเรียกซ้ำแบบหางเช่นตัวอย่างของฉันใน OCaml ด้านล่างไม่ได้กระจายสแต็ก
Anton Trunov

1
@AntonTrunov แน่ใจว่ามันเป็นชื่อของเว็บไซต์มากกว่าความพยายามที่จะแม่นยำ
wvxvw

คำตอบ:


17

อันดับแรกคำอธิบายศัพท์: ตำแหน่งลบและบวกมาจากตรรกะ พวกเขาจะเกี่ยวกับ assymetry ในตรรกะ connectives: ในBทำงานแตกต่างจากB สิ่งที่คล้ายกันเกิดขึ้นในทฤษฎีหมวดหมู่ซึ่งเราบอกว่าcontravariantและcovariantแทนที่จะเป็นลบและบวกตามลำดับ ในวิชาฟิสิกส์พวกเขาพูดถึงปริมาณที่ทำหน้าที่ "covariantly" และ "contravariantly เช่นกันดังนั้นนี่เป็นปรากฏการณ์ทั่วไปมากโปรแกรมเมอร์อาจคิดว่าพวกเขาเป็น" อินพุต "และ" เอาท์พุท "ABAB

ตอนนี้เข้าสู่ประเภทข้อมูลอุปนัย

คิดว่าประเภทข้อมูลอุปนัยเป็นชนิดของโครงสร้างเกี่ยวกับพีชคณิต: การก่อสร้างมีการดำเนินงานที่ใช้องค์ประกอบของTเป็นข้อโต้แย้งและการผลิตองค์ประกอบใหม่ของT นี่คล้ายกับพีชคณิตสามัญ: การเพิ่มจะใช้สองตัวเลขและสร้างตัวเลขTTT

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

  • ถ้าcเป็นค่าคงที่เราสามารถคิดว่ามันเป็นฟังก์ชั่นunit -> Tหรือเท่ากัน(empty -> T) -> T,
  • ถ้าcเป็นเอกเราสามารถคิดว่ามันเป็นฟังก์ชั่นT -> Tหรือเท่ากัน(unit -> T) -> T,
  • ถ้าcเป็นไบนารีเราสามารถคิดว่ามันเป็นฟังก์ชั่นT -> T -> Tหรือเท่ากันT * T -> Tหรือเท่ากัน(bool -> T) -> T,
  • ถ้าเราต้องการนวกรรมิกcที่ใช้เจ็ดข้อโต้แย้งเราสามารถดูมันเป็นฟังก์ชั่น(seven -> T) -> Tที่sevenบางประเภทที่กำหนดไว้ก่อนหน้านี้มีเจ็ดองค์ประกอบ
  • เรายังสามารถมีผู้สร้างซึ่งจะมีข้อโต้แย้งวท์หลายอย่างมากมายที่จะเป็นฟังก์ชั่นc(nat -> T) -> T

ตัวอย่างเหล่านี้แสดงให้เห็นว่ารูปแบบทั่วไปของตัวสร้างควรเป็น

c : (A -> T) -> T

ที่เราเรียกarityของและเราคิดว่าเป็นตัวสร้างที่จะนำข้อโต้แย้ง -many ประเภทการผลิตองค์ประกอบของAccATT

นี่คือสิ่งที่สำคัญมาก: ต้องกำหนด arities ก่อนที่เราจะกำหนดTหรืออื่น ๆ ที่เราไม่สามารถบอกได้ว่าผู้สร้างควรจะทำอะไร หากมีคนพยายามที่จะมีตัวสร้าง

broken: (T -> T) -> T

ถ้าอย่างนั้นคำถาม "มีอาร์กิวเมนต์กี่ข้อbroken?" ไม่มีคำตอบที่ดี คุณอาจลองตอบด้วยคำว่า "มันต้องใช้Tอาร์กิวเมนต์หลายอย่าง" แต่จะไม่ทำเพราะTยังไม่ได้กำหนด เราอาจพยายามออกจาก cunundrum โดยใช้ทฤษฎี fixed-point แฟนซีเพื่อค้นหาประเภทTและฟังก์ชั่นการฉีด(T -> T) -> Tและจะประสบความสำเร็จ แต่เราจะทำลายหลักการการเหนี่ยวนำTไปพร้อมกัน ดังนั้นจึงเป็นความคิดที่ดีที่จะลองทำสิ่งนี้

λโวลต์λโวลต์cB

c : B * (A -> T) -> T

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

c : (∑ (x : B), A x -> T) -> T

นี่คือรูปแบบสุดท้ายของตัวสร้างสำหรับประเภทอุปนัย นอกจากนี้ยังเป็นสิ่งที่ประเภท W อย่างแม่นยำ รูปแบบเป็นเรื่องธรรมดามากที่เราต้องการเพียงคอนสตรัคเตอร์เดียวc! แน่นอนถ้าเรามีสองคน

d' : (∑ (x : B'), A' x -> T) -> T
d'' : (∑ (x : B''), A'' x -> T) -> T

จากนั้นเราสามารถรวมเข้าเป็นหนึ่งเดียว

d : (∑ (x : B), A x -> T) -> T

ที่ไหน

B := B' + B''
A(inl x) := A' x
A(inr x) := A'' x

โดยวิธีการถ้าเราแกงรูปแบบทั่วไปที่เราเห็นว่ามันเทียบเท่า

c : ∏ (x : B), ((A x -> T) -> T)

ซึ่งใกล้เคียงกับสิ่งที่ผู้คนเขียนลงไปในผู้ช่วยพิสูจน์ ผู้ช่วยพิสูจน์ให้เราจดสิ่งก่อสร้างด้วยวิธีที่สะดวก แต่สิ่งเหล่านี้เทียบเท่ากับแบบทั่วไปข้างต้น (แบบฝึกหัด!)


1
ขอบคุณ Andrej อีกครั้งหลังอาหารกลางวันของฉันนี่จะเป็นสิ่งที่ยากที่สุดสำหรับฉันที่จะแยกแยะ ไชโย
Pushpa

9

การเกิดขึ้นครั้งแรกของBadถูกเรียกว่า 'ลบ' เพราะมันหมายถึงการโต้แย้งฟังก์ชั่นคือตั้งอยู่ทางด้านซ้ายของลูกศรฟังก์ชั่น (ดูประเภท Recursive ฟรีโดย Philip Wadler) ผมคิดว่าที่มาของคำว่า 'ตำแหน่งเชิงลบ' เกิดจากความคิดของcontravariance ( 'Contra' หมายถึงตรงข้าม)

ไม่อนุญาตให้มีการกำหนดประเภทไว้ในตำแหน่งลบเนื่องจากสามารถเขียนโปรแกรมที่ไม่สิ้นสุดการใช้งานได้เช่นการทำบรรทัดฐานให้ปกติล้มเหลวในสถานะที่มีอยู่ (ดูเพิ่มเติมที่ด้านล่างนี้) อย่างไรก็ตามนี่คือเหตุผลสำหรับชื่อของกฎ 'positivity ที่เข้มงวด': เราไม่อนุญาตให้มีตำแหน่งเชิงลบ

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

สิ่งสำคัญคือต้องเข้าใจว่าคำจำกัดความต่อไปนี้ไม่ได้ละเมิดกฎความเป็นบวกอย่างเข้มงวด

data Good : Set where
  good : Good → Good → Good

กฎใช้เฉพาะกับข้อโต้แย้งคอนสตรัค (ซึ่งมีทั้งGoodในกรณีนี้) และไม่ให้ตัวสร้างตัวเอง (เห็นอดัม Chlipala เรื่อง " การเขียนโปรแกรมได้รับการรับรองกับประเภทขึ้นอยู่กับ ")

อีกตัวอย่างหนึ่งที่ละเมิด positivity ที่เข้มงวด:

data Strange : Set where
  strange : ((Bool → Strange) → (ℕ → Strange)) → Strange
                       ^^     ^
            this Strange is   ...this arrow
            to the left of... 

คุณอาจต้องการตรวจสอบคำตอบนี้เกี่ยวกับตำแหน่งเชิงลบ


เพิ่มเติมเกี่ยวกับการไม่ยกเลิก ... หน้าเว็บที่คุณอ้างถึงมีคำอธิบายบางอย่าง (พร้อมด้วยตัวอย่างใน Haskell):

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

นี่คือตัวอย่างที่คล้ายคลึงกันใน Ocaml ซึ่งแสดงวิธีการใช้พฤติกรรมแบบเรียกซ้ำโดยไม่ต้อง (!)โดยใช้การเรียกซ้ำโดยตรง:

type boxed_fun =
  | Box of (boxed_fun -> boxed_fun)

(* (!) in Ocaml the 'let' construct does not permit recursion;
   one have to use the 'let rec' construct to bring 
   the name of the function under definition into scope
*)
let nonTerminating (bf:boxed_fun) : boxed_fun =
  match bf with
    Box f -> f bf

let loop = nonTerminating (Box nonTerminating)

nonTerminatingฟังก์ชั่น "unpacks" ฟังก์ชั่นจากการโต้แย้งและแอปเปิ้มันในการโต้แย้งเดิม สิ่งที่สำคัญคือระบบประเภทส่วนใหญ่ไม่อนุญาตให้ส่งผ่านฟังก์ชั่นให้กับตัวเองดังนั้นคำที่ต้องการf fจะไม่พิมพ์ดีดเนื่องจากไม่มีประเภทสำหรับfตอบสนองตัวพิมพ์ตรวจสอบ หนึ่งในเหตุผลที่ระบบประเภทถูกนำเสนอคือการปิดการใช้งานด้วยตนเอง (ดูที่นี่ )

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

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

Fixpoint loop (n : nat) : False = loop n

จากนั้นloop 0จะพิมพ์การตรวจการให้loop 0 : Falseดังนั้นภายใต้การติดต่อของ Curry-Howard หมายความว่าเราพิสูจน์ข้อเสนอที่ผิดพลาด

Upshot : กฎ positivity ที่เข้มงวดสำหรับคำจำกัดความอุปนัยช่วยป้องกันการคำนวณที่ไม่สิ้นสุดซึ่งเป็นหายนะสำหรับตรรกะ


ตอนนี้ฉันสับสน ข้อมูลพิเศษดี: ตั้งค่าที่ดี: ดี→ดี→ เราจะพยายามทำความเข้าใจและกลับมาในอีกหนึ่งชั่วโมง /
Pushpa

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