我想知道我是否可以吃蛋糕而且也吃KnownNats
。我可以编写使用可能同时为Nats
和KnownNats
(UnknownNats
的{{1}}的代码吗?
例如,如果我有一个依赖类型的向量SomeNats
,我是否可以编写在编译和运行时已知大小的代码?事情是,我不想复制静态和动态大小的“事物”的整个代码。而且我不希望通过在数据结构中存储大小来丢失静态保证。
回答AndrásKovács:
我的具体用例是从磁盘读取图像(幸运的是固定大小),然后从中提取补丁,所以基本上我有一个函数Vec (n :: Nat) a
a extractPatch :: (KnownNat w2, KnownNat h2) => Image w1 h1 a -> Patch w2 h2
和{{1}是常见Image
类型的实例。
如果我不知道图像大小,我必须在“运行时类型”中对其进行编码。只是想知道。
答案 0 :(得分:9)
这里有一些有趣的东西......
{-# 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
让我们分解这里发生的事情。
Integer
SomeNat
- 类型的值,如果输入为负,则模式匹配失败。对于这样一个简单的例子,处理该错误只是妨碍了。case
表达式,使用ScopedTypeVariables
将(静态未知的)Nat
- kinded类型绑定到类型变量n
。Bar
作为其类型变量创建n
值,然后使用它进行操作。