我发现有两种方法可以在运行时使用TypeLits和Proxy(Data.Proxy和GHC.TypeLits)或Singletons(或者Singletons)在运行时将Integer提升为Nat(或者KnownNat,我还没有得到区别)。 Data.Singletons)。在下面的代码中,您可以看到如何使用这两种方法:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Main where
import Prelude hiding (replicate)
import Data.Proxy (Proxy(Proxy))
import Data.Monoid ((<>))
import Data.Singletons (SomeSing(..), toSing)
import GHC.TypeLits
import Data.Singletons.TypeLits
import Data.Vector.Sized (Vector, replicate)
main :: IO ()
main = playingWithTypes 8
playingWithTypes :: Integer -> IO ()
playingWithTypes nn = do
case someNatVal nn of
Just (SomeNat (proxy :: Proxy n)) -> do
-- let (num :: Integer) = natVal proxy
-- putStrLn $ "Some num: " <> show num
putStrLn $ "Some vector: " <> show (replicate 5 :: Vector n Int)
Nothing -> putStrLn "There's no number, the integer was not a natural number"
case (toSing nn :: SomeSing Nat) of
SomeSing (SNat :: Sing n) -> do
-- let (num :: Integer) = natVal (Proxy :: Proxy n)
-- putStrLn $ "Some num: " <> show num
putStrLn $ "Some vector: " <> show (replicate 5 :: Vector n Int)
TypeLits的文档表明它不应该被开发人员使用,但是单身人士不会捕获给定的Integer不是自然数的情况(即,运行playingWithTypes 8
运行没有错误,但是当我们尝试从非自然数创建一个Singleton时,playingWithTypes (-2)
失败了。
那么,将Integer
推广到Nat
的“标准”方式是什么?或者,使用TypeLits和Proxy或Singletons进行推广的最佳方法是什么?
答案 0 :(得分:7)
Nat
(或KnownNat
,我还没有得到区别)
Nat
是一种类型级自然数。它没有任期级别的居民。这个想法是GHC将任何自然数字提升到类型级别,并赋予其类型Nat
。
KnownNat
是一种约束,关于某种类Nat
,它的实现见证了如何将类Nat
的事物转换为术语级Integer
。 GHC为所有类型级别居民KnownNat
1 自动创建Nat
个实例。
也就是说,即使每个n :: Nat
(类型n
的读取类型Nat
)上都有KnownNat
个实例 1 ,仍然需要写出约束。
你真的吗?在一天结束时,我找到了两种方法来推广
Integer
到Nat
Nat
在今天的GHC中只是神奇的。 singletons
使用同样的魔法。在引擎盖下,uses someNatVal
。
那么,将
Integer
推广到Nat
的“标准”方式是什么?或者,使用GHC.TypeLits
和Proxy
或单身人士推广的最佳方法是什么?
没有标准的方法。我的看法是:当你可以负担其依赖性足迹时使用singletons
,否则使用GHC.TypeLits
。 singletons
的优势在于,SingI
类型类可以方便地进行基于归纳的分析,同时还依赖于GHC的特殊Nat
类型。
1 正如评论中所指出的那样, Nat
类的每个居民都没有KnownNat
个实例。例如,Any Nat :: Nat
其中Any
是one from GHC.Exts
。只有居民0
,1
,2
,......才有KnownNat
个实例。