ความแตกต่างของตัวแปรระดับสูงมีประโยชน์หรือไม่


16

ฉันค่อนข้างมั่นใจว่าทุกคนคุ้นเคยกับวิธีการทั่วไปของแบบฟอร์ม:

T DoSomething<T>(T item)

ฟังก์ชั่นนี้เรียกว่า parametrically polymorphic (PP) โดยเฉพาะอันดับ 1 PP

สมมติว่าวิธีนี้สามารถแสดงโดยใช้ฟังก์ชั่นวัตถุของรูปแบบ:

<T> : T -> T

นั่นคือ<T>หมายความว่าใช้พารามิเตอร์ประเภทเดียวและT -> Tหมายความว่าใช้พารามิเตอร์ประเภทเดียวTและส่งกลับค่าประเภทเดียวกัน

ต่อไปนี้จะเป็นฟังก์ชัน PP อันดับ 2:

(<T> : T -> T) -> int 

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

คุณลักษณะนี้หายากมากในบรรดาภาษาการเขียนโปรแกรม แม้แต่ Haskell ก็ไม่อนุญาตตามค่าเริ่มต้น

มันมีประโยชน์หรือไม่ มันสามารถอธิบายพฤติกรรมที่ยากที่จะอธิบายเป็นอย่างอื่นได้หรือไม่?

นอกจากนี้มันหมายความว่าอะไรที่จะต้องมีความคิดมาก ? (ในบริบทนี้)


1
น่าสนใจ TypeScript เป็นหนึ่งในภาษาหลักที่มีการสนับสนุน PP-Rank เต็มรูปแบบ ตัวอย่างเช่นต่อไปนี้เป็นรหัส TypeScript ที่ถูกต้อง:let sdff = (g : (f : <T> (e : T) => void) => void) => {}
GregRos

คำตอบ:


11

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

f :: (forall a. Show a => a -> Int) -> (Int, Int)
f g = (g "one", g 2)

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

f (length . show)
f (const 42)

แต่นี่ไม่ใช่:

f length
f succ

หนึ่งโปรแกรมที่มีประโยชน์โดยเฉพาะอย่างยิ่งในการใช้การกำหนดขอบเขตของประเภทการบังคับใช้การกำหนดขอบเขตของค่า สมมติว่าเรามีอ็อบเจกต์ประเภทAction<T>ซึ่งเป็นตัวแทนการกระทำที่เราสามารถเรียกใช้เพื่อสร้างผลลัพธ์ประเภทTเช่นอนาคตหรือการโทรกลับ

T runAction<T>(Action<T>)

runAction :: forall a. Action a -> a

ทีนี้สมมติว่าเรามีสิ่งActionที่สามารถจัดสรรResource<T>วัตถุ:

Action<Resource<T>> newResource<T>(T)

newResource :: forall a. a -> Action (Resource a)

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

เราสามารถใช้ชนิดที่สูงขึ้นการจัดอันดับที่จะบรรลุนี้โดยการเพิ่มพารามิเตอร์SไปResourceและActionประเภทซึ่งมีทั้งหมดนามธรรมมันหมายถึง“ขอบเขต” Actionของ ตอนนี้ลายเซ็นของเราคือ:

T run<T>(<S> Action<S, T>)
Action<S, Resource<S, T>> newResource<T>(T)

runAction :: forall a. (forall s. Action s a) -> a
newResource :: forall s a. a -> Action s (Resource s a)

ตอนนี้เมื่อเราให้เราจะมั่นใจได้ว่าเพราะ“ขอบเขต” พารามิเตอร์เป็น polymorphic อย่างเต็มที่ก็ไม่สามารถหนีร่างกายของ-So ค่าของชนิดที่ใช้ใด ๆเช่นเช่นเดียวกันไม่สามารถหลบหนี!runActionAction<S, T>SrunActionSResource<S, int>

(ใน Haskell นี้เป็นที่รู้จักกันSTmonad ที่runActionเรียกว่าrunST, Resourceเรียกว่าSTRefและnewResourceที่เรียกว่าnewSTRef.)


STmonad เป็นตัวอย่างที่น่าสนใจมาก คุณสามารถยกตัวอย่างเพิ่มเติมได้ไหมว่าเมื่อใดความหลากหลายที่มีอันดับสูงกว่าจะเป็นประโยชน์?
GregRos

@GregRos: มันยังมีประโยชน์กับการดำรงอยู่ ในHaxlเรามีสิ่งมีชีวิตที่คล้ายdata Fetch d = forall a. Fetch (d a) (MVar a)กันซึ่งเป็นคู่ของคำขอไปยังแหล่งข้อมูลdและสล็อตที่เก็บผลลัพธ์ ผลลัพธ์และสล็อตต้องมีประเภทการจับคู่ แต่ประเภทนั้นถูกซ่อนไว้ดังนั้นคุณสามารถมีรายการคำขอที่แตกต่างกันไปยังแหล่งข้อมูลเดียวกัน fetch :: (forall a. d a -> IO a) -> [Fetch d] -> IO ()ตอนนี้คุณสามารถใช้อันดับที่สูงขึ้นมีหลายรูปแบบที่จะเขียนฟังก์ชั่นที่เรียกร้องขอทั้งหมดให้ฟังก์ชั่นที่เรียกหนึ่ง:
Jon Purdy

8

ความหลากหลายของอันดับที่สูงกว่านั้นมีประโยชน์อย่างมาก ใน System F (ภาษาหลักของภาษา FP ที่พิมพ์แล้วที่คุณคุ้นเคย) สิ่งนี้เป็นสิ่งจำเป็นสำหรับการยอมรับ "encodings ของโบสถ์ที่พิมพ์" ซึ่งเป็นวิธีการเขียนโปรแกรมของ System F หากไม่มีสิ่งเหล่านี้ระบบ F ก็ไร้ประโยชน์อย่างสมบูรณ์

ใน System F เรากำหนดตัวเลขเป็น

Nat = forall c. (c -> c) -> c -> c

นอกจากนี้ยังมีประเภท

plus : Nat -> Nat -> Nat
plus l r = Λ t. λ (s : t -> t). λ (z : t). l s (r s z)

ซึ่งเป็นประเภทอันดับที่สูงขึ้น ( forall c.ปรากฏในลูกศรเหล่านั้น)

เรื่องนี้เกิดขึ้นในที่อื่นด้วย ตัวอย่างเช่นหากคุณต้องการระบุว่าการคำนวณเป็นรูปแบบการส่งต่อที่เหมาะสม (google "codensity haskell") คุณจะต้องใช้สิ่งนี้เป็น

type CPSed A = forall c. (A -> c) -> c

แม้แต่การพูดคุยเกี่ยวกับประเภทที่ไม่มีใครอาศัยอยู่ใน System F ต้องมีความหลากหลายในระดับสูง

type Void = forall a. a 

ความยาวและสั้นของการเขียนฟังก์ชันในระบบแบบบริสุทธิ์ (System F, CoC) ต้องมีความหลากหลายในระดับสูงกว่าหากเราต้องการจัดการกับข้อมูลที่น่าสนใจ

โดยเฉพาะอย่างยิ่งในระบบ F การเข้ารหัสเหล่านี้จำเป็นต้อง "ไม่จำเป็น" ซึ่งหมายความว่าforall a.การประเมินมากกว่าอย่างทุกประเภท สิ่งนี้รวมถึงประเภทที่เรากำหนด ในforall a. aสิ่งนั้นaสามารถยืนหยัดได้forall a. aอีกครั้ง! ในภาษาเช่น ML นี่ไม่ใช่กรณีพวกเขาถูกกล่าวว่าเป็น "predicative" เนื่องจากตัวแปรประเภทระบุปริมาณเฉพาะชุดประเภทที่ไม่มีตัวนับ (เรียกว่า monotypes) ความหมายของเราplusimpredicativity จำเป็นเช่นกันเพราะเรา instantiated cในl : Natจะเป็นNat!

ในที่สุดฉันอยากจะพูดถึงหนึ่งในเหตุผลสุดท้ายที่คุณต้องการทั้งความไม่มั่นใจและความแตกต่างของอันดับที่สูงขึ้นแม้จะเป็นภาษาที่มีการเรียกซ้ำแบบสุ่ม (โดยไม่เหมือนกับ System F) ใน Haskell มี Monad สำหรับเอฟเฟกต์ที่เรียกว่า "state thread monad" แนวคิดคือสถานะเธรด monad อนุญาตให้คุณกลายพันธุ์สิ่งต่าง ๆ แต่ต้องหลบหนีเพราะผลลัพธ์ของคุณไม่ได้ขึ้นอยู่กับสิ่งที่ไม่แน่นอน ซึ่งหมายความว่าการคำนวณ ST นั้นบริสุทธิ์อย่างน่าสังเกต เพื่อบังคับใช้ข้อกำหนดนี้เราใช้ polymorphism อันดับที่สูงขึ้น

runST :: forall a. (forall s. ST s a) -> a

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

โดยวิธีการถ้าคุณสนใจที่จะเรียนรู้เกี่ยวกับทฤษฎีประเภทฉันขอแนะนำให้ลงทุนในหนังสือที่ดีหรือสอง เป็นการยากที่จะเรียนรู้สิ่งนี้ในบิตและชิ้นส่วน ฉันขอแนะนำหนังสือเล่มหนึ่งของ Pierce หรือ Harper เกี่ยวกับทฤษฎี PL โดยทั่วไป (และองค์ประกอบบางอย่างของทฤษฎีชนิด) หนังสือ "หัวข้อขั้นสูงในประเภทและภาษาการเขียนโปรแกรม" ยังครอบคลุมทฤษฎีประเภทจำนวนมาก ในที่สุด "การเขียนโปรแกรมในทฤษฎีประเภทของ Martin Lof" เป็นการอธิบายที่ดีมากเกี่ยวกับทฤษฎีประเภท intensional Martin Lof ที่ระบุไว้


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