【问题标题】:Besides as-pattern, what else can @ mean in Haskell?除了 as-pattern,@在 Haskell 中还有什么含义?
【发布时间】:2020-08-12 09:18:39
【问题描述】:

我目前正在研究 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,但这段代码显然不是那种情况。我查看了https://www.haskell.org/definition/haskell2010.pdf 上的在线教程、博客甚至Haskell 2010 语言报告。这个问题根本没有答案。

更多代码sn-ps也可以在这个项目中使用@这种方式找到:

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

我非常感谢您对此提供的任何帮助。

【问题讨论】:

  • 这些是type applications。另见this Q&A。您还可以查看将它们引入代码的commit
  • 非常感谢您的链接!这些正是我正在寻找的。令人惊讶的是,您甚至可以识别代码的提交!非常感谢。只是好奇你是怎么找到它的? @MikaelF
  • Github 有自己的git blame 接口,它会告诉你最后一次修改每一行的提交。
  • 非常感谢这个有用的提示:)
  • @MichaelLitchard 很高兴您能从中受益。我感谢善良的人们花时间帮助我。希望答案也可以帮助其他人。

标签: haskell symbols operator-keyword as-pattern


【解决方案1】:

@n 是现代 Haskell 的一项高级功能,通常不会在 LYAH 之类的教程中涉及,也无法在报告中找到。

它被称为type application,是一个 GHC 语言扩展。为了理解它,考虑这个简单的多态函数

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 的上下文中),所以我们通常只向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)

这更好,但即使n 不是5,仍然允许调用foo 5 x y。不好。相反,

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

【讨论】:

猜你喜欢
  • 2021-10-06
  • 2012-05-01
  • 1970-01-01
  • 2019-07-12
  • 2016-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多