我能否知道KnownNat n暗示KnownNat(n * 3)等?

时间:2015-09-29 08:33:48

标签: haskell singleton-type

我使用V中的linear处理此形状的数据类型:

type Foo n = V (n * 3) Double -> Double

将其修复为n非常重要,因为我希望能够确保在编译时传递正确数量的元素。这是我的计划的一部分,已经运作良好,独立于我在这里做的事情。

对于任何KnownNat n,我可以生成满足我的程序所需行为的Foo n。出于这个问题的目的,它可能是一个愚蠢的东西

mkFoo :: KnownNat (n * 3) => Foo n
mkFoo = sum

或者对于更有意义的示例,它可以生成相同长度的随机V,并在两者上使用dot。这里的KnownNat约束是多余的,但实际上,需要做Foo。我制作一个Foo并将其用于我的整个程序(或多个输入),这样就可以保证每当我使用它时,我都会使用相同长度的东西,以及Foo指令的结构。

最后,我有一个为Foo输入的函数:

bar :: KnownNat (n * 3) => Proxy n -> [V (n * 3) Double]

bar 实际上是我使用n * 3作为类型函数的原因,而不是仅仅手动展开它。原因是bar可能通过使用三个长度为n的向量并将它们全部作为长度为n * 3的向量附加在一起来完成其工作。此外,n在语义上比n * 3更有意义。这也让我不允许n之类的不正确值,它们不是3的倍数等等。

现在,只要我在开头定义了一个类型同义词,一切正常:

type N = 5

然后我可以将Proxy :: Proxy N传递给bar,并使用mkFoo :: Foo N。一切都很好。

-- works fine
doStuff :: [Double]
doStuff = let inps = bar (Proxy :: Proxy N)
          in  map (mkFoo :: Foo N) inps

但现在我希望能够在运行时通过从文件或命令行参数加载信息来调整N

我尝试通过调用reflectNat

来做到这一点
doStuff :: Integer -> Double
doStuff n = reflectNat 5 $ \pn@(Proxy :: Proxy n) ->
              let inps = bar (Proxy :: Proxy n)
              in  map (mkFoo :: Foo n) inps

但是...... barmkFoo需要KnownNat (n * 3),但reflectNat只是给了我KnownNat n

有什么方法可以概括reflectNat让我满足foo的证明吗?

2 个答案:

答案 0 :(得分:3)

我发布了另一个答案,因为它更直接,编辑之前的胜利没有意义。

实际上使用诀窍(如果不是Edward Kmett发明的那样推广),来自reflections parent::__construct

reifyNat

所以当你运行它时:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
import GHC.TypeLits
import Data.Proxy
import Unsafe.Coerce

newtype MagicNat3 r = MagicNat3 (forall (n :: Nat). KnownNat (n * 3) => Proxy n -> r)

trickValue :: Integer -> Integer
trickValue = (*3)

-- No type-level garantee that the function will be called with (n * 3)
-- you have to believe us
trick :: forall a n. KnownNat n => Proxy n -> (forall m. KnownNat (m * 3) => Proxy m -> a) -> a
trick p f = unsafeCoerce (MagicNat3 f :: MagicNat3 a) (trickValue (natVal p)) Proxy

test :: forall m. KnownNat (m * 3) => Proxy m -> Integer
test _ = natVal (Proxy :: Proxy (m * 3))

诀窍是基于这样一个事实:在GHC中,一个成员类词典(如λ *Main > :t trick (Proxy :: Proxy 4) test :: Integer trick (Proxy :: Proxy 4) test :: Integer :: Integer λ *Main > trick (Proxy :: Proxy 4) test :: Integer 12 )由成员本身表示。在KnownNat情况下,结果是KnownNat。所以我们只在那里Integer。通用量化使其从外部发出声音。

答案 1 :(得分:0)

你的问题不是很具描述性,所以我会尽量让自己感觉空白:

我们假设var textvalue = parseInt($('#ty' + (i + 1).toString()).text) if (textvalue < 0) { $('#ty' + (i + 1).toString()).addClass("fa-level-down"); $('#ty' + (i + 1).toString()).removeClass("fa-level-up"); } else { $('#ty' + (i + 1).toString()).addClass("fa-level-up"); $('#ty' + (i + 1).toString()).removeClass("fa-level-down"); } Blah n

我还假设Proxy n是一种使用术语级自然数来调用普遍量化(超过类型reflectNat)函数的方法。

我不会比编写自己的Nat提供更好的方法

reflectNat

或者,使用{-# LANGUAGE RankNTypes #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-} import GHC.TypeLits import Data.Proxy data Vec a (n :: Nat) where Nil :: Vec a 0 Cons :: a -> Vec a n -> Vec a (1 + n) vecToList :: Vec a n -> [a] vecToList Nil = [] vecToList (Cons h t) = h : vecToList t repl :: forall n a. KnownNat n => Proxy n -> a -> Vec a n repl p x = undefined -- this is a bit tricky with Nat from GHC.TypeLits, but possible foo :: forall (n :: Nat). KnownNat (1 + n) => Proxy n -> Vec Bool (1 + n) foo _ = repl (Proxy :: Proxy (1 + n)) True -- Here we have to write our own version of 'reflectNat' providing right 'KnownNat' instances -- so we can call `foo` reflectNat :: Integer -> (forall n. KnownNat (1 + n) => Proxy (n :: Nat) -> a) -> a reflectNat = undefined test :: [Bool] test = reflectNat 5 $ \p -> vecToList (foo p) 即可使用singletons。然后类型将是不同的

SomeSing

即。而不是魔法字典reflectNat :: Integer -> (forall (n :: Nat). SomeSing (n :: Nat) -> a) -> a 你有单独的具体价值。因此,在KnownNat中,您需要明确地构建foo,给定SomeSing (1 + n) - 这非常简单。

在运行时,SomeSing n字典和KnownNat值将传递给数字值,并且在这种情况下显式是恕我直言.p)