结合`SomeNat`和`Nat`

时间:2015-09-01 12:22:32

标签: haskell types

我正在关注问题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

1 个答案:

答案 0 :(得分:1)

第一个例子是someNatVal的纯粹魔法,它制作了一个类型级Nat和知识。在第二个示例中,您了解n,但您询问的是n+5。这有两个原因无效。

第一个问题是类型系统会将所有“额外”实例视为不连贯。也就是说,它需要知道KnownNat 2KnownNat 7以及KnownNat n => KnownNat (n + 5)KnownNat n => KnownNat (5 + n)等等。这些实例都会相互冲突,因此编译器需要有关如何处理这种情况的特殊内置知识,而且一切都会非常痛苦。

另一个问题是TypeLits非常简单。它们似乎没有你期望的类型级自然的有用的归纳结构。

由于这些问题,似乎出于多种目的,必须放弃简单有效的TypeLit机制,以支持慢速方式,手工建立的一元自然数,你可以在{ {3}}。你需要单独的函数来处理值和类型级别的计算,但至少类型检查器将确保它们正确匹配,并且有一些模板Haskell试图使它更容易一次写入。