我正在关注问题Can I have an unknown KnownNat?
中列出的示例我想对代码进行一些小改动。
原始代码是
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
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)
main :: IO ()
main = do
i <- readLn
let Just someNat = someNatVal i
case someNat of
SomeNat (_ :: Proxy n) -> do
let a :: Bar n
a = Bar "as"
print $ bar a
按预期工作。我想进行更改,我在其中修改了类型级别n
。
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
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)
main :: IO ()
main = do
i <- readLn
let Just someNat = someNatVal i
case someNat of
SomeNat (_ :: Proxy n) -> do
let a :: Bar (n + 5)
a = Bar "as"
print $ bar a
我收到的错误消息是
Could not deduce (KnownNat (n + 5)) arising from a use of ‘bar’
from the context (KnownNat n)
bound by a pattern with constructor
SomeNat :: forall (n :: Nat). KnownNat n => Proxy n -> SomeNat,
in a case alternative
at Blag.hs:19:9-30
In the second argument of ‘($)’, namely ‘bar a’
In a stmt of a 'do' block: print $ bar a
In the expression:
do { let a :: Bar (n + 5)
a = Bar "as";
print $ bar a }
Failed, modules loaded: none.
为什么编译器不能从KnownNat (n + 5)
中推断KnownNat n
?
答案 0 :(得分:1)
第一个例子是someNatVal
的纯粹魔法,它制作了一个类型级Nat
和知识。在第二个示例中,您了解n
,但您询问的是n+5
。这有两个原因无效。
第一个问题是类型系统会将所有“额外”实例视为不连贯。也就是说,它需要知道KnownNat 2
和KnownNat 7
以及KnownNat n => KnownNat (n + 5)
和KnownNat n => KnownNat (5 + n)
等等。这些实例都会相互冲突,因此编译器需要有关如何处理这种情况的特殊内置知识,而且一切都会非常痛苦。
另一个问题是TypeLits
非常简单。它们似乎没有你期望的类型级自然的有用的归纳结构。
由于这些问题,似乎出于多种目的,必须放弃简单有效的TypeLit
机制,以支持慢速方式,手工建立的一元自然数,你可以在{ {3}}。你需要单独的函数来处理值和类型级别的计算,但至少类型检查器将确保它们正确匹配,并且有一些模板Haskell试图使它更容易一次写入。