As-pattern'in yanı sıra, Haskell'de başka ne ifade edebilir?


15

Şu anda Haskell'i inceliyorum ve kriptografik algoritmalar uygulamak için Haskell'i kullanan bir projeyi anlamaya çalışıyorum. Online Great Good için Haskell Öğrenin'i okuduktan sonra bu projedeki kodu anlamaya başlıyorum. Sonra ben "@" sembolü ile aşağıdaki kod takılı kalmış bulundu:

-- | 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

Burada randomMtx aşağıdaki gibi tanımlanır:

-- | 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

Ve PRFKey aşağıda tanımlanmıştır:

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

Bulabildiğim tüm bilgi kaynakları, @ 'nin as-pattern olduğunu söylüyor, ancak bu kod parçası görünüşte böyle değil. Ben online eğitim, bloglar ve hatta kontrol ettikten Haskell 2010 dil raporunu en https://www.haskell.org/definition/haskell2010.pdf . Bu sorunun cevabı yok.

Bu projede @ kullanılarak bu şekilde daha fazla kod snippet'i bulunabilir:

-- | 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))

Bu konuda her türlü yardımı çok takdir ediyorum.


11
Bunlar tip uygulamalar . Ayrıca bkz Bu Q ve A . Onları koda sokan taahhüde de bakabilirsiniz .
MikaelF

Linkler için çok teşekkürler! Bunlar tam da aradığım şey. Şaşırtıcı bir şekilde, kodun taahhüdünü bile belirlersiniz! Bunun için çok teşekkürler. Sadece nasıl bulduğunu merak ediyor musun? @MikaelF
SigurdW

2
Github'ın git suçu için kendi arayüzü vardır , bu da size her satırın en son hangi değişiklik yapıldığını söyleyecektir.
MikaelF

Bu yararlı ipucu için çok teşekkürler :)
SigurdW

1
@MichaelLitchard Bundan faydalanabildiğiniz için çok mutluyum. Bana yardım etmek için zaman harcayan kibar insanlara minnettarım. Umarım cevap başkalarına da yardımcı olabilir.
SigurdW

Yanıtlar:


16

Bu @n, modern Haskell'in, genellikle LYAH gibi öğreticiler tarafından kapsanmayan ve Rapor'da bulunmayan gelişmiş bir özelliğidir.

Buna tip uygulaması denir ve GHC dil uzantısıdır. Bunu anlamak için bu basit polimorfik fonksiyonu düşünün

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

Sezgisel olarak arama dupşu şekilde çalışır:

  • Arayan bir seçer türü a
  • Arayan bir seçer değeri x , önceden seçilen Çeşidia
  • dup sonra bir tür değeri ile cevaplar (a,a)

Bir anlamda dupiki argüman alır: tür ave değer x :: a. Bununla birlikte, GHC genellikle türü çıkarabilir a(örneğin x, kullandığımız bağlamdan veya bağlamdan dup), bu nedenle genellikle yalnızca bir argümanı dup, yanix . Örneğin,

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

Şimdi, ya aaçıkça geçmek istersek ? Bu durumda, TypeApplicationsuzantıyı açabilir ve yazabiliriz

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

Not edin @...Türleri taşıyan değişkenleri (değerleri değil). Bunlar sadece derleme zamanında var olan bir şeydir - çalışma zamanında argüman mevcut değildir.

Bunu neden istiyoruz? Bazen xetrafta bir şey yok ve derleyiciyi doğru seçmesi için prod etmek istiyoruz a. Örneğin

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

Tür uygulamaları genellikle belirsiz türler veya tür aileleri gibi tür çıkarımı GHC için olanaksız kılan diğer bazı uzantılarla birlikte kullanışlıdır. Bunları tartışmayacağım, ancak bazen güçlü tip seviyesi özellikleri kullanırken derleyiciye gerçekten yardımcı olmanız gerektiğini anlayabilirsiniz.

Şimdi, özel durumunuz hakkında. Ben kütüphaneyi bilmiyorum tüm ayrıntıları yok, ama çok büyük olasılıkla sizin de bu ndoğal sayısı değerine bir tür temsil tür düzeyinde . Burada, yukarıda belirtilenler artı DataKinds, belki GADTsve bazı daktilo makineleri gibi oldukça gelişmiş uzantılara dalıyoruz. Her şeyi açıklayamasam da, umarım bazı temel bilgiler verebilirim. sezgisel,

foo :: forall n . some type using n

argüman olarak @n, çalışma zamanında aktarılmayan bir tür derleme zamanı doğaldır. Yerine,

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

sürer @nbirlikte bir ile, (derleme zamanı) ispat o nkarşılar sınırlamak C n. Sonuncusu, gerçek değerini ortaya çıkarabilecek bir çalışma zamanı bağımsız değişkenidir n. Gerçekten, sizin durumunuzda, sanırım benzeyen bir şeyiniz var

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

bu da kodun doğal olarak tip-seviyesini terim-seviyesine getirmesine, esas olarak "tip" e "değer" olarak erişmesine izin verir. (Bu tür bir "belirsiz" olarak kabul edilir, bu arada - gerçekten @nde belirsizleştirmek gerekir .)

Son olarak n, daha sonra bunu terim seviyesine dönüştürürsek neden tür seviyesinde geçmek istesin ki? Gibi fonksiyonları yazmak daha kolay olmazdı

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

daha hantal yerine

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

Dürüst cevap: evet, daha kolay olurdu. Ancak, ntür düzeyinde olması, derleyicinin daha statik denetimler gerçekleştirmesine olanak tanır. Örneğin, bir türün "tamsayı modulo n" u temsil etmesini ve bunların eklenmesine izin vermesini isteyebilirsiniz . sahip olan

data Mod = Mod Int  -- Int modulo some n

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

çalışır, ancak hiçbir onay yoktur xve yaynı modüllü olmaktadırlar. Eğer dikkatli olmazsak, elma ve portakal ekleyebiliriz. Bunun yerine yazabiliriz

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)

ki bu daha iyidir, ancak olmasa foo 5 x ybile aramaya izin verir . İyi değil. Yerine,n5

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))

şeylerin yanlış gitmesini önler. Derleyici statik olarak her şeyi kontrol eder. Kodun kullanımı daha zordur, evet, ancak bir bakıma kullanımı zorlaştırmak bütün mesele: kullanıcının yanlış modüllerden bir şey eklemeyi denemesini imkansız kılmak istiyoruz.

Sonuç: bunlar çok gelişmiş uzantılardır. Yeni başlayan biriyseniz, bu tekniklere doğru yavaşça ilerlemeniz gerekecektir. Kısa bir çalışmadan sonra onları kavrayamıyorsanız cesaretiniz kırılmasın, biraz zaman alır. Her seferinde küçük bir adım atın, her bir özellik için noktasını anlamak için bazı alıştırmaları çözün. Ve takılıp kaldığınızda her zaman StackOverflow'a sahip olacaksınız :-)


Ayrıntılı açıklama için çok teşekkür ederim! Sorunumu gerçekten çözdü ve sanırım cevabı kendim bulmak için daha fazla zamana ihtiyacım olacak. Ayrıca öneriniz için teşekkür ederiz!
SigurdW
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.