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
これらはタイプのアプリケーションですこちらのQ&Aもご覧ください。また、コードにそれらを導入したコミットを確認することもできます。
MikaelF

リンクありがとうございます!これらはまさに私が探しているものです。驚いたことに、コードのコミットを特定することさえできます!本当にありがとう。どうやってそれを見つけるのか知りたいですか?@MikaelF
SigurdW

2
Githubにはgit blameへの独自のインターフェースがあり、各行が最後に変更されたコミットがわかります。
MikaelF

この便利なヒントをありがとう:)
SigurdW

1
@MichaelLitchardあなたがそれから利益を得ることができてとてもうれしいです。私を手伝うために時間を費やしてくれる親切な人々に感謝しています。答えが他の人にも役立つことを願っています。
SigurdW

回答:


16

それ @nは、現代のHaskellの高度な機能であり、通常LYAHのようなチュートリアルではカバーされておらず、レポートからも見つけることができません。

これは型アプリケーションと呼ばれ、GHC言語拡張です。それを理解するために、この単純な多態性関数を考えてください

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

直感的な呼び出しdupは次のように機能します。

  • 呼び出し側が選択したタイプを a
  • 呼び出し側は、選択 x、以前に選択されたタイプのをa
  • dup 次に、typeの値で答えます (a,a)

ある意味でdupは、型aと値という2つの引数を取りますx :: a。ただし、GHCは通常、型を推測できますa(たとえば、からx、またはを使用しているコンテキストからdup)。そのため、通常はに1つの引数のみを渡しdupますx。たとえば、

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

これにより、コードは本質的に型レベルを用語レベルに自然にして、「型」に「値」としてアクセスできます。(ちなみに、上記のタイプは「あいまい」なタイプと見なされます。本当に@n明確にする必要があります。)

最後nに、後でそれを用語レベルに変換する場合、なぜ型レベルで渡す必要があるのですか?次のような関数を簡単に書くのは簡単ではないでしょう

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

より面倒な代わりに

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

正直な答えは次のとおりです。はい、簡単です。ただし、n型レベルで持つことにより、コンパイラはより多くの静的チェックを実行できます。たとえば、型が「整数モジュロ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)

動作xyますが、それと同じ係数のチェックはありません。注意しないと、リンゴやオレンジを追加する可能性があります。代わりに

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 ynでない場合でも呼び出すことができ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.