我可以拥有一个未知的 KnownNat 吗?

Can I have an unknown KnownNat?

我想知道关于KnownNats我是否可以吃我的蛋糕。我可以编写使用可能同时是 KnownNatsUnknownNatsNats 的代码吗(SomeNats?)。

例如,如果我有一个依赖类型的向量 Vec (n :: Nat) a,如果大小在编译和运行时已知,我是否可以编写既能工作的代码?事情是我不想复制静态和动态大小 "things" 的整个代码。而且我不想通过在数据结构中存储大小来失去静态保证。

编辑

安德拉斯·科瓦奇的回答:

我的具体用例是从磁盘读取图像(幸好大小固定)然后从中提取补丁,所以基本上我有一个函数 extractPatch :: (KnownNat w2, KnownNat h2) => Image w1 h1 a -> Patch w2 h2 a 其中 ImagePatch 是常见 Mat (w :: Nat) (h :: Nat) 类型的实例。

如果我不知道图像大小,我将不得不在 "runtime types" 中对其进行编码。只是想知道。

这里有一些可能有趣的东西...

{-# LANGUAGE DataKinds, KindSignatures, ScopedTypeVariables #-}

import GHC.TypeLits
import Data.Proxy

data Bar (n :: Nat) = Bar String deriving Show

bar :: KnownNat n => Bar n -> (String, Integer)
bar b@(Bar s) = (s, natVal b)

好吧,这很没有意义。但这是使用 KnownNat 获取编译时信息的示例。但是由于 GHC.TypeLits 中的其他功能,它也可以与 运行 时间信息一起使用。

只需将其添加到上面的代码中,然后尝试一下。

main :: IO ()
main = do
    i <- readLn
    let Just someNat = someNatVal i
    case someNat of
       SomeNat (_ :: Proxy n) -> do
           let b :: Bar n
               b = Bar "hello!"
           print $ bar b

让我们分解一下这里发生的事情。

  1. 从标准输入读取 Integer
  2. 从中创建一个 SomeNat 类型的值,如果输入为负,则模式匹配失败。对于这样一个简单的示例,处理该错误只会成为阻碍。
  3. 这才是真正的魔法。使用 case 表达式进行模式匹配,使用 ScopedTypeVariables 将(静态未知的)Nat 类型绑定到类型变量 n.
  4. 最后,创建一个 Bar 值,并将特定的 n 作为其类型变量,然后用它做一些事情。