การแทนค่าตัวแปรที่ถูกผูกไว้กับฟังก์ชั่นจากการใช้งานเพื่อยึดประสาน


11

ปัญหาของการเป็นตัวแทนของตัวแปรที่ถูกผูกไว้ในไวยากรณ์และโดยเฉพาะอย่างยิ่งที่ทดแทนการหลีกเลี่ยงการจับภาพเป็นที่รู้จักกันดีและมีจำนวนของการแก้ปัญหา: ชื่อตัวแปรที่มีความเท่าเทียมกันอัลฟา, ดัชนี de Bruijn

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

λx.(λy.xy)

จะเขียนλและฟังก์ชั่นจะ map แรกแรกλและครั้งที่สองที่สองλ ดังนั้นมันเหมือนกับดัชนี de Bruijn เพียงแทนที่จะต้อง "นับλ s" เมื่อคุณกลับออกจากคำศัพท์เพื่อค้นหาสารยึดเกาะที่สอดคล้องกันคุณเพียงแค่ประเมินฟังก์ชั่น (หากแสดงสิ่งนี้เป็นโครงสร้างข้อมูลในการดำเนินการฉันจะคิดว่าการจัดเตรียมออบเจ็กต์คำศัพท์แต่ละตัวแปรด้วยตัวชี้ / การอ้างอิงอย่างง่ายไปยังออบเจ็กต์คำที่สอดคล้องกัน)λ.(λ.)λλλ

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

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


2
ฉันไม่รู้เรื่องข้อเสีย อาจเป็นทางการ (เช่นในผู้ช่วยพิสูจน์) หนักกว่าหรือเปล่า ฉันไม่แน่ใจ ... สิ่งที่ฉันรู้ก็คือว่าไม่มีอะไรผิดปกติทางเทคนิค: วิธีการดูข้อตกลงแลมบ์ดานี้เป็นสิ่งที่แนะนำโดยการเป็นตัวแทนของพวกเขาในฐานะอวนพิสูจน์หลักฐานดังนั้นคนที่รู้ตัวว่าสุทธิ ตลอดเวลา. แต่คนที่รับการพิสูจน์มาแล้วว่าหายากมาก :-) ดังนั้นอาจเป็นเรื่องของประเพณีจริงๆ PS: ฉันเพิ่มแท็กที่เกี่ยวข้องอย่างหลวม ๆ สองสามอย่างเพื่อให้คำถามปรากฏขึ้น (หวังว่า)
Damiano Mazza

วิธีนี้ไม่เทียบเท่ากับไวยากรณ์นามธรรมที่มีลำดับสูงกว่า (กล่าวคือเป็นตัวยึดประสานเป็นฟังก์ชันในภาษาโฮสต์) ในแง่หนึ่งการใช้ฟังก์ชั่นเป็นเครื่องผูกกำหนดพอยน์เตอร์ให้เป็นตัวประสานโดยปริยายในการเป็นตัวแทนของการปิด
Rodolphe Lepigre

2
@RodolpheLepigre ฉันไม่คิดอย่างนั้น โดยเฉพาะอย่างยิ่งความเข้าใจของฉันคือ HOAS นั้นถูกต้องก็ต่อเมื่อ metatheory นั้นค่อนข้างอ่อนแอในขณะที่วิธีการนี้ถูกต้องใน metatheory โดยพลการ
Mike Shulman

3
ขวาดังนั้นแต่ละเครื่องผูกใช้ชื่อตัวแปรที่ไม่ซ้ำกัน (ภายในทรี) (ตัวชี้จะเป็นหนึ่งโดยอัตโนมัติ) นี่คือการประชุม Barendregt แต่เมื่อคุณทดแทนคุณต้องสร้างใหม่ (ใน C) สิ่งที่คุณกำลังทดแทนเพื่อให้มีชื่อที่ไม่ซ้ำกัน มิฉะนั้น (โดยทั่วไป) คุณกำลังใช้พอยน์เตอร์เดียวกันสำหรับหลาย subtrees และสามารถจับตัวแปรได้ การสร้างใหม่คือการเปลี่ยนชื่ออัลฟา สันนิษฐานว่ามีบางสิ่งที่คล้ายกันเกิดขึ้นขึ้นอยู่กับลักษณะเฉพาะของการเข้ารหัสของต้นไม้ตามที่ตั้งไว้?
Dan Doel

3
@DanDoel Ah น่าสนใจ ฉันคิดว่ามันเห็นได้ชัดดังนั้นเป็นไปไม่จำเป็นต้องมีการกล่าวขวัญว่าคุณจะลดลงในแยกสำเนาคำที่ถูกเปลี่ยนตัวในการเกิดขึ้นของตัวแปรมันถูกแทนสำหรับทุก มิฉะนั้นคุณจะไม่มีแผนผังต้นไม้อีกต่อไป! ฉันไม่คิดว่าการคัดลอกนี้จะเป็นการเปลี่ยนชื่อเป็นอัลฟา แต่ตอนนี้คุณเห็นแล้วว่าฉันเห็นได้
Mike Shulman

คำตอบ:


11

คำตอบของ Andrej และŁukaszให้คะแนนดี แต่ฉันต้องการเพิ่มความคิดเห็นเพิ่มเติม

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

  • Donald Knuth (1970) ตัวอย่างของความหมายที่เป็นทางการ ในการประชุมสัมมนาเกี่ยวกับความหมายของภาษาอัลกอริทึม , E. Engeler (ed.), บันทึกการบรรยายในวิชาคณิตศาสตร์ 188, สปริงเกอร์

(λy.λz.yz)x

แผนภาพของ Knuth สำหรับ $ (\ lambda y. \ lambda z.yz) x $

การแสดงภาพกราฟิกของคำแลมบ์ดานี้ได้ศึกษาอย่างอิสระ (และลึกซึ้งยิ่งขึ้น) ในสองวิทยานิพนธ์ในต้นปี 1970 ทั้งโดย Christopher Wadsworth (1971, Semantics และ Pragmatics ของ Lambda-แคลคูลัส ) และ Richard Statman (1974, ความซับซ้อนของโครงสร้าง ของการพิสูจน์ ) ทุกวันนี้ไดอะแกรมดังกล่าวมักถูกเรียกว่า "graph-graphs" (ดูตัวอย่างกระดาษนี้ )

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

α


10

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

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

ในแคลคูลัสแลมบ์ดาทั่วไปที่พิมพ์, การลดลงและการหดตัว, ตรงกันข้ามกับกฎอื่น ๆ , ไม่มีไวยากรณ์

Γt:TΓ,x:At:TW
Γ,x1:A,x2:At:TΓ,x:At:TC

ลองเพิ่มรูปแบบ:

Γt:TΓ,x:AWx(t):TW
Γ,x1:A,x2:At:TΓ,x:ACxx1,x2(t):TC

Cab,c()ab,c

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

จากความซับซ้อนของอัลกอริทึมตอนนี้เราสามารถใช้พอยน์เตอร์ไม่ได้มาจากตัวแปรหนึ่งไปยังตัวยึด แต่จากตัวยึดประสานไปเป็นตัวแปรและมีการแทนที่ในเวลาที่แน่นอน

นอกจากนี้การปฏิรูปนี้ช่วยให้เราสามารถติดตามการลบคัดลอกและแบ่งปันด้วยความซื่อสัตย์มากขึ้น หนึ่งสามารถเขียนกฎที่คัดลอก (หรือลบ) คำที่เพิ่มขึ้นในขณะที่การแบ่งปันคำ มีหลายวิธีที่จะทำเช่นนั้น ในการตั้งค่าการ จำกัด บางชนะค่อนข้างน่าแปลกใจ

นี่คือการใกล้เคียงกับหัวข้อของอวนโต้ตอบ, ผู้ประสานการโต้ตอบ, การแทนที่อย่างชัดเจน, ตรรกะเชิงเส้น, การประเมินผลที่ดีที่สุดของ Lamping, กราฟที่ใช้ร่วมกัน, logics แสงและอื่น ๆ

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


6

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

แต่ฉันยินดีที่ได้เห็นการทดสอบ! คุณสามารถใช้lambdaและนำไปใช้กับโครงสร้างข้อมูลของคุณ (OCaml มีตัวชี้พวกเขาเรียกว่าการอ้างอิง ) มากขึ้นหรือน้อยลงคุณเพียงแค่ต้องแทนที่syntax.mlและnorm.mlใช้เวอร์ชันของคุณ นั่นคือน้อยกว่า 150 บรรทัดของรหัส


ขอบคุณ! ฉันยอมรับว่าฉันไม่ได้คิดมากเกี่ยวกับการนำไปใช้ แต่ส่วนใหญ่เกี่ยวกับความสามารถในการพิสูจน์ทางคณิตศาสตร์โดยไม่ต้องกังวลเกี่ยวกับการทำบัญชี de Bruijn หรือการเปลี่ยนชื่ออัลฟา แต่มีโอกาสใดบ้างที่การใช้งานสามารถเก็บการแชร์หน่วยความจำบางส่วนโดยไม่ทำสำเนา "จนกว่าจะจำเป็น" คือจนกว่าจะมีการคัดลอกจะแตกต่างจากกัน?
Mike Shulman

β(λx.e1)e2e1e2

2
เกี่ยวกับการพิสูจน์ทางคณิตศาสตร์ตอนนี้ฉันได้ผ่านการทำข้อสอบอย่างเป็นทางการของไวยากรณ์ประเภททฤษฎีแล้วประสบการณ์ของฉันคือข้อดีที่ได้รับเมื่อเราพูดถึงการตั้งค่าทั่วไปและทำให้เป็นนามธรรมมากขึ้นไม่ใช่เมื่อเราทำให้เป็นรูปธรรมมากขึ้น ตัวอย่างเช่นเราสามารถกำหนดไวยากรณ์ด้วย "วิธีที่ดีในการรักษาผูกพัน" เมื่อเราทำเช่นนั้นยากที่จะทำผิดพลาด ฉันได้สร้างทฤษฎีประเภทด้วยกรงเล็บที่มีดัชนีเดอบรูจน์ มันไม่น่ากลัวเกินไปโดยเฉพาะถ้าคุณมีประเภทที่ต้องพึ่งพาซึ่งจะป้องกันคุณจากการทำสิ่งไร้สาระ
Andrej Bauer

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

@MikeShulman สำหรับอัลกอริธึมของความซับซ้อนที่สมเหตุสมผล (ระดับประถมศึกษา) (จำนวนมากในการคัดลอกและลบ) สิ่งที่เรียกว่า 'ส่วนนามธรรม' ของการลดที่เหมาะสมที่สุดของ Lamping ไม่ได้ทำสำเนาจนกว่าจะจำเป็น ส่วนที่เป็นนามธรรมยังเป็นส่วนที่ไม่ขัดแย้งซึ่งตรงข้ามกับอัลกอริธึมเต็มรูปแบบซึ่งต้องการคำอธิบายประกอบที่สามารถครอบงำการคำนวณได้
Łukasz Lew

5

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

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

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

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


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

(ตกลงว่ามันคุ้มค่าที่จะลอง ... แม้ว่าฉันจะไม่แปลกใจถ้าใครมีอยู่แล้วในบริบทของการพิสูจน์แบบอวน - กราฟ / λ-กราฟ)
Noam Zeilberger


5

λLazy.t

โดยรวมแล้วฉันคิดว่ามันเป็นตัวแทนที่ยอดเยี่ยม แต่มันเกี่ยวข้องกับการทำบัญชีกับพอยน์เตอร์เพื่อหลีกเลี่ยงการเชื่อมโยงที่มีผลผูกพัน มันเป็นไปได้ที่จะเปลี่ยนรหัสเพื่อใช้ฟิลด์ที่ไม่แน่นอนที่ฉันเดาได้ แต่การเข้ารหัสใน Coq นั้นจะน้อยกว่าโดยตรง ฉันยังคงเชื่อว่าสิ่งนี้คล้ายกับ HOAS มากถึงแม้ว่าโครงสร้างของตัวชี้จะชัดเจน อย่างไรก็ตามการมีอยู่ของLazy.tนัยก็เป็นไปได้ที่จะมีการประเมินรหัสในเวลาที่ผิด นี่ไม่ใช่กรณีในรหัสของฉันเป็นเพียงการทดแทนของตัวแปรที่มีตัวแปรอาจเกิดขึ้นในforceเวลา (และไม่ได้ประเมินตัวอย่าง)

(* Representation of a term of the λ-calculus. *)
type term =
  | FVar of string      (* Free variable  *)
  | BVar of bvar        (* Bound variable *)
  | Appl of term * term (* Application    *)
  | Abst of abst        (* Abstraction    *)

(* A bound variable is a pointer to the corresponding binder. *)
and bvar = abst

(* A binder is represented as its body in which the bound variable points to
   the binder itself. Note that we need to use a thunk to be able to work
   underneath a binder (for substitution, evaluation, ...). A name can be
   given for easy printing, but no renaming is done. Only “visual capture”
   can happen since pointers are established the right way, even if names
   can clash. *)
and abst = { body : term Lazy.t ; name : string }

(* Terms can be built with recursive values for abstractions. *)

(* Krivine's notation is used for application (function in parentheses). *)

let id    : term = (* λx.x        *)
  Abst(let rec id = {body = lazy (BVar(id)); name = "x"} in id)

let idid  : term = (* (λx.x) λx.x *)
  Appl(id, id)

let delta : term = (* λx.(x) x *)
  Abst(let rec d = {body = lazy (Appl(BVar(d), BVar(d))); name = "x" } in d)

let weird : term = (* (λx.x) λy.(λx.(x) x) (C) y *)
  Appl(id, Abst(let rec x = {body = lazy (Appl(delta, Appl(FVar("C"),
    BVar(x)))); name = "y"} in x))

let omega : term = (* (λx.(x) x) λx.(x) x *)
  Appl(delta, delta)

(* Printing function is immediate. *)
let rec print : out_channel -> term -> unit = fun oc t ->
  match t with
  | FVar(x)   -> output_string oc x
  | BVar(x)   -> output_string oc x.name
  | Appl(t,u) -> Printf.fprintf oc "(%a) %a" print t print u
  | Abst(f)   -> Printf.fprintf oc "λ%s.%a" f.name print (Lazy.force f.body)

(* Substitution of variable [x] by [v] in the term [t]. Occurences of [x] in
   [t] are identified using physical equality ([BVar] case). The subtle case
   is [Abst], because we need to reestablish the physical link between the
   binder and the variable it binds. *)
let rec subst_var : bvar -> term -> term -> term = fun x t v ->
  match t with
  | FVar(_)   -> t
  | BVar(y)   -> if y == x then v else t
  | Appl(t,u) -> Appl(subst_var x t v, subst_var x u v)
  | Abst(f)   ->
      (* First compute the new body. *)
      let fv = subst_var x (Lazy.force f.body) v in
      (* Reestablish the physical link, using [subst_var] itself again. This
         requires a second traversal of the term. We could probably do both
         at once, but who cares the complexity is linear in [t] anyway. *)
      Abst(let rec g = {f with body = lazy (subst_var f fv (BVar(g)))} in g)

(* Actual substitution function. *)
let subst : abst -> term -> term = fun f v ->
  subst_var f (Lazy.force f.body) v

(* Normalization function (all the way, even under binders). *)
let rec eval : term -> term = fun t ->
  match t with
  | Appl(t,u) ->
      begin
        let v = eval u in
        match eval t with
        | Abst(f) -> eval (subst f v)
        | t       -> Appl(t,v)
      end
  | Abst(f)   ->
      (* Actual computation in the body. *)
      let fv = eval (Lazy.force f.body) in
      (* Here, the physical link is reestablished, but it is important to note
         that the computation of evaluation is done above. So the part below
         only takes a linear time in the size of the normal form of the body
         of the abstraction. *)
      Abst(let rec g = {f with body = lazy (subst_var f fv (BVar(g)))} in g)
  | _         ->
      t

let _ = Printf.printf "id         = %a\n%!" print id
let _ = Printf.printf "eval id    = %a\n%!" print (eval id)

let _ = Printf.printf "idid       = %a\n%!" print idid
let _ = Printf.printf "eval idid  = %a\n%!" print (eval idid)

let _ = Printf.printf "delta      = %a\n%!" print delta
let _ = Printf.printf "eval delta = %a\n%!" print (eval delta)

let _ = Printf.printf "omega      = %a\n%!" print omega
(* The following obviously loops. *)
(*let _ = Printf.printf "eval omega = %a\n%!" print (eval omega)*)

let _ = Printf.printf "weird      = %a\n%!" print weird
let _ = Printf.printf "eval weird = %a\n%!" print (eval weird)

(* Output produced:
id         = λx.x
eval id    = λx.x
idid       = (λx.x) λx.x
eval idid  = λx.x
delta      = λx.(x) x
eval delta = λx.(x) x
omega      = (λx.(x) x) λx.(x) x
weird      = (λx.x) λy.(λx.(x) x) (C) y
eval weird = λy.((C) y) (C) y
*)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.