如何在编译时获取GHC.TypeLits.TypeError类型错误而不是在运行时?

时间:2017-07-13 10:31:25

标签: haskell ghc type-families type-level-computation

直到现在,我假设GHC在编译时执行类型级别函数(类型族)。因此,应在编译时发出由TypeError类型系列触发的错误消息。

在以下示例中,我在运行时收到类型错误。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import GHC.TypeLits

type family If c t e where
    If 'True  t e = t
    If 'False t e = e

type family EqSymbol (a :: Symbol) (b :: Symbol) where
    EqSymbol a a = 'True
    EqSymbol a b = 'False

type family Lookup (x :: Symbol) (l :: [(Symbol,t)]) :: t where
    Lookup k '[]             = TypeError (Text "Key not found: "  :<>: Text k)
    Lookup k  ('(x,a) ': ls) = If (EqSymbol k x)  a  (Lookup k ls)

type TList =  '[ '("foo", Int), '("bar", String)]

test1 :: Lookup "foo" TList
test1 = undefined

test2 :: Lookup "bar" TList
test2 = undefined

test3 :: Lookup "baz" TList
test3 = undefined

对于函数test3,类型级函数Lookup的评估应该给出类型错误,因为 baz 不是TList中的键。

GHCi在没有类型错误的情况下加载代码:

GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
Prelude> :l SO.hs
[1 of 1] Compiling Main             ( SO.hs, interpreted )
Ok, modules loaded: Main.

查询函数的类型test1test2给出了预期的结果:

*Main> :t test1
test1 :: Int
*Main> :t test2
test2 :: [Char]

查询函数test3的类型会给出类型错误,只有当我尝试评估 test3函数时才会执行TypeError函数:

*Main> :t test3
test3 :: (TypeError ...)
*Main> test3

<interactive>:5:1: error:
    • Key not found: baz
    • When checking the inferred type
        it :: (TypeError ...)

我需要做什么才能获得编译时错误?

1 个答案:

答案 0 :(得分:3)

编译模块时不会导致错误的原因是懒惰。这与print (if True then 1 else error "Mearg")不会导致任何问题的原因基本相同:因为else分支实际上从未使用,所以(可证明!)没有办法提高错误表达可能会影响结果。如果愿意,错误只发生在替代宇宙中。

同样,您永远不会使用test3可以(好吧,不是!)供应的类型信息。即你从不评估Lookup "baz" TList结果,不是在编译时,也不是在运行时。所以没有错误!

在任何使用这种类型系列的真实程序中, 会对具体类型信息感兴趣,并执行

之类的操作
show0 :: (Show q, Num q) => q -> String
show0 q = show $ 0`asTypeOf`q

main :: IO ()
main = putStrLn $ show0 test3

这确实会导致编译时错误(实际上由于某种原因导致两次):

sagemuej@sagemuej-X302LA:~$ ghc /tmp/wtmpf-file2798.hs 
[1 of 1] Compiling Main             ( /tmp/wtmpf-file2798.hs, /tmp/wtmpf-file2798.o )

/tmp/wtmpf-file2798.hs:35:19: error:
    • Key not found: baz
    • In the second argument of ‘($)’, namely ‘show0 test3’
      In the expression: putStrLn $ show0 test3
      In an equation for ‘main’: main = putStrLn $ show0 test3
   |
35 | main = putStrLn $ show0 test3
   |                   ^^^^^^^^^^^

/tmp/wtmpf-file2798.hs:35:19: error:
    • Key not found: baz
    • In the second argument of ‘($)’, namely ‘show0 test3’
      In the expression: putStrLn $ show0 test3
      In an equation for ‘main’: main = putStrLn $ show0 test3
   |
35 | main = putStrLn $ show0 test3
   |                   ^^^^^^^^^^^