TypeLits 或 Singletons:在运行时将 `Integer` 提升为 `KnownNat` (`Nat`)

TypeLits or Singletons: Promoting an `Integer` to `KnownNat` (`Nat`) at Runtime

我找到了两种在运行时将 Integer 提升为 Nat(或 KnownNat,我还没有区分)的方法,要么使用 TypeLits 和 Proxy(Data.Proxy 和 GHC.TypeLits),或单例 (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 的文档表明开发人员不应使用它,但单例不会捕获给定整数不是自然数的情况(即 运行 playingWithTypes 8 运行没有错误,但是 playingWithTypes (-2) 当我们尝试从非自然数创建单例时失败)。

那么,将 Integer 提升为 Nat 的 "standard" 方法是什么?或者最好的推广方式是什么,使用 TypeLits 和 Proxy,还是 Singletons?

Nat (or KnownNat, I don't get the distintion yet)

Nat是那种类型级别的自然数。它没有任期级别的居民。这个想法是 GHC 将任何自然数提升到类型级别,并赋予它类型 Nat.

KnownNat 是一个约束,在某种事物 Nat 上,其实现见证了如何将事物 Nat 转换为术语级别 Integer。 GHC 自动为 all 类型 Nat1.[=52= 类型的居民创建 KnownNat 的实例]

也就是说,即使每个 n :: Nat(读取类型 n 类型 Nat)都有一个 KnownNat 实例1,你还需要写出约束条件。

I've found two ways to promote an Integer to a Nat

你真的吗?归根结底,Nat 在今天的 GHC 中简直是神奇的。 singletons 利用了同样的魔法。在引擎盖下,它 uses someNatVal.

So, what is the "standard" way to promote an Integer to a Nat? Or what is the best approach to promote, using GHC.TypeLits and Proxy, or singletons?

没有标准的方法。我的看法是:当你能负担得起它的依赖足迹时使用 singletons,否则使用 GHC.TypeLitssingletons 的优点是 SingI 类型 class 可以方便地进行基于归纳的分析,同时仍然依赖于 GHC 的特殊 Nat 类型。


1 正如评论中指出的那样,并非 每个 Nat 类型的居民都有 KnownNat 实例。例如,Any Nat :: Nat 其中 Anyone from GHC.Exts。只有居民012、...有KnownNat个实例。