นอกจาก as-pattern @ ยังมีความหมายอะไรใน Haskell


15

ฉันกำลังศึกษา Haskell อยู่ในขณะนี้และพยายามทำความเข้าใจโครงการที่ใช้ Haskell เพื่อใช้อัลกอริทึมการเข้ารหัสลับ หลังจากอ่านLearn You a Haskell for Great Goodออนไลน์ฉันเริ่มเข้าใจโค้ดในโครงงานนั้น จากนั้นฉันก็พบว่าฉันติดอยู่ที่รหัสต่อไปนี้ด้วยสัญลักษณ์ "@":

-- | Generate an @n@-dimensional secret key over @rq@.
genKey :: forall rq rnd n . (MonadRandom rnd, Random rq, Reflects n Int)
       => rnd (PRFKey n rq)
genKey = fmap Key $ randomMtx 1 $ value @n

นี่คือ randomMtx ถูกกำหนดดังนี้:

-- | A random matrix having a given number of rows and columns.
randomMtx :: (MonadRandom rnd, Random a) => Int -> Int -> rnd (Matrix a)
randomMtx r c = M.fromList r c <$> replicateM (r*c) getRandom

และ PRFKey ถูกกำหนดไว้ด้านล่าง:

-- | A PRF secret key of dimension @n@ over ring @a@.
newtype PRFKey n a = Key { key :: Matrix a }

แหล่งข้อมูลทั้งหมดที่ฉันสามารถหาพูดได้ว่า @ เป็น as-pattern แต่โค้ดชิ้นนี้ไม่ได้เป็นอย่างนั้น ผมได้ตรวจสอบกวดวิชาออนไลน์บล็อกและแม้กระทั่งรายงานภาษา Haskell 2010ที่https://www.haskell.org/definition/haskell2010.pdf ไม่มีคำตอบสำหรับคำถามนี้

โค้ดเพิ่มเติมสามารถพบได้ในโครงการนี้โดยใช้ @ ด้วยวิธีนี้เช่นกัน:

-- | Generate public parameters (\( \mathbf{A}_0 \) and \(
-- \mathbf{A}_1 \)) for @n@-dimensional secret keys over a ring @rq@
-- for gadget indicated by @gad@.
genParams :: forall gad rq rnd n .
            (MonadRandom rnd, Random rq, Reflects n Int, Gadget gad rq)
          => rnd (PRFParams n gad rq)
genParams = let len = length $ gadget @gad @rq
                n   = value @n
            in Params <$> (randomMtx n (n*len)) <*> (randomMtx n (n*len))

ฉันซาบซึ้งในความช่วยเหลืออย่างมาก


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

ขอบคุณมากสำหรับลิงค์! นี่คือสิ่งที่ฉันกำลังมองหา น่าแปลกที่คุณระบุการกระทำของรหัสด้วย! ขอบคุณมากสำหรับสิ่งนั้น แค่อยากรู้ว่าคุณเจอมันได้อย่างไร? @MikaelF
SigurdW

2
Github มีอินเทอร์เฟซของตัวเองเพื่อตำหนิ gitซึ่งจะบอกคุณว่าการกระทำแต่ละบรรทัดได้รับการแก้ไขครั้งสุดท้าย
MikaelF

ขอบคุณมากสำหรับเคล็ดลับที่มีประโยชน์นี้ :)
SigurdW

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

คำตอบ:


16

นั่น@nเป็นคุณลักษณะขั้นสูงของ Haskell ทันสมัยซึ่งโดยทั่วไปแล้วจะไม่ครอบคลุมโดยบทเรียนเช่น LYAH และไม่สามารถพบรายงานได้

มันเรียกว่าแอปพลิเคชันประเภทและเป็นส่วนขยายภาษา GHC เพื่อความเข้าใจลองพิจารณาฟังก์ชั่น polymorphic แบบง่าย ๆ

dup :: forall a . a -> (a, a)
dup x = (x, x)

การเรียกใช้งานได้โดยสัญชาตญาณdupดังนี้:

  • โทรเลือกประเภท a
  • โทรเลือกมูลค่า xของชนิดได้รับการแต่งตั้งก่อนหน้านี้a
  • dup จากนั้นตอบด้วยค่าประเภท (a,a)

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

dup True    :: (Bool, Bool)
dup "hello" :: (String, String)
...

ทีนี้ถ้าหากเราต้องการผ่านaอย่างชัดเจนล่ะ ในกรณีนี้เราสามารถเปิดTypeApplicationsส่วนขยายและเขียน

dup @Bool True      :: (Bool, Bool)
dup @String "hello" :: (String, String)
...

หมายเหตุ@...ข้อโต้แย้งที่ถือประเภท (ไม่ใช่ค่า) สิ่งเหล่านี้เป็นสิ่งที่มีอยู่ในเวลารวบรวมเท่านั้น - ณ เวลาอาร์กิวเมนต์ไม่มีอยู่

ทำไมเราต้องการสิ่งนั้น ดีบางครั้งไม่มีรอบและเราต้องการที่จะแยงคอมไพเลอร์ที่จะเลือกที่เหมาะสมx aเช่น

dup @Bool   :: Bool -> (Bool, Bool)
dup @String :: String -> (String, String)
...

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

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

foo :: forall n . some type using n

ใช้เวลาเป็นอาร์กิวเมนต์@nชนิดของการคอมไพล์เวลาธรรมชาติซึ่งไม่ได้ผ่านที่รันไทม์ แทน,

foo :: forall n . C n => some type using n

ใช้เวลา@n(เวลารวบรวม) ร่วมกับหลักฐานที่น่าพอใจข้อ จำกัดn หลังเป็นอาร์กิวเมนต์เวลาทำงานซึ่งอาจจะเปิดเผยมูลค่าที่แท้จริงของC n nในกรณีของคุณฉันคิดว่าคุณมีบางสิ่งที่คล้ายราง

value :: forall n . Reflects n Int => Int

ซึ่งช่วยให้รหัสเพื่อนำธรรมชาติระดับประเภทไปยังคำศัพท์ระดับการเข้าถึง "ประเภท" เป็น "ค่า" (ประเภทข้างต้นถือว่าเป็น "ไม่ชัดเจน" อย่างใดอย่างหนึ่งโดยวิธี - คุณจำเป็นต้องมี@ndisambiguate)

ในที่สุด: ทำไมเราจึงควรต้องการผ่านnระดับประเภทหากเราในภายหลังแปลงเป็นระดับคำ คงไม่ง่ายกว่าที่จะเขียนฟังก์ชั่นเช่น

foo :: Int -> ...
foo n ... = ... use n

แทนที่จะยุ่งยากมากขึ้น

foo :: forall n . Reflects n Int => ...
foo ... = ... use (value @n)

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

data Mod = Mod Int  -- Int modulo some n

foo :: Int -> Mod -> Mod -> Mod
foo n (Mod x) (Mod y) = Mod ((x+y) `mod` n)

ทำงาน แต่ไม่มีการตรวจสอบว่าxและyมีโมดูลัสเดียวกัน เราอาจเพิ่มแอปเปิ้ลและส้มถ้าเราไม่ระวัง เราสามารถเขียนแทน

data Mod n = Mod Int  -- Int modulo n

foo :: Int -> Mod n -> Mod n -> Mod n
foo n (Mod x) (Mod y) = Mod ((x+y) `mod` n)

ซึ่งดีกว่า แต่ยังคงสามารถโทรfoo 5 x yได้แม้ในขณะที่nไม่5อยู่ ไม่ดี. แทน,

data Mod n = Mod Int  -- Int modulo n

-- a lot of type machinery omitted here

foo :: forall n . SomeConstraint n => Mod n -> Mod n -> Mod n
foo (Mod x) (Mod y) = Mod ((x+y) `mod` (value @n))

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

สรุป: นี่คือส่วนขยายขั้นสูงมาก หากคุณเป็นมือใหม่คุณจะต้องพัฒนาเทคนิคเหล่านี้อย่างช้าๆ อย่าท้อใจถ้าคุณไม่สามารถเข้าใจพวกเขาหลังจากการศึกษาสั้น ๆ มันใช้เวลาพอสมควร ทำทีละขั้นตอนเล็ก ๆ แก้แบบฝึกหัดให้แต่ละคุณสมบัติเข้าใจจุดนั้น และคุณจะมี StackOverflow เสมอเมื่อคุณติดขัด :-)


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