如何在Haskell中触发类型族模式匹配错误?

时间:2016-05-23 16:45:58

标签: haskell type-level-computation

Haskell是否能够指示类型系列匹配错误?例如,使用封闭类型族:

type family Testf a where
  Testf Char   = IO ()
  Testf String = IO ()

Testf Int的类型只是Testf Int。编译器不会生成任何类型的错误。如果没有匹配,是否可以让它生成一个?

2 个答案:

答案 0 :(得分:7)

不可能。良好的家庭类型应用程序从不会自行触发错误。相反,当我们尝试使用未减少的类型族表达式时,我们只会遇到类型错误。

我们可以使用包含错误消息的自定义类型,以使错误更清晰:

import GHC.TypeLits

data Error (msg :: Symbol) -- a type-level error message

type family Testf a where
  Testf Char   = IO ()
  Testf String = IO ()
  Testf x      = Error "No match for Testf"

现在,当我们尝试使用Error msg键入非未定义的值时,GHC会抛出错误并打印出我们的消息。

从GHC 8.0开始,我们可以使用TypeError以更好的方式打印我们的消息:

{-# language DataKinds #-}
import GHC.TypeLits

type family Testf a where
  Testf Char   = IO ()
  Testf String = IO ()
  Testf x      = TypeError (Text "No matching case for Testf")

这将打印:

Notes.hs:18:5: error: …
    • No matching case for Testf
    • In the expression: ...

然而,这仍然只会引起使用错误:

type T = Testf Int -- this typechecks

x :: T
x = () -- we throw error only here

答案 1 :(得分:4)

在8.0之前的GHC中这是不可能的,但the (as of this writing) just-released GHC 8.0.1增加了对custom type errors的支持。

这个想法是,就像函数error :: String -> a在任意类型中存在错误一样,我们现在有in GHC.TypeLits类型族

type family TypeError (msg :: ErrorMessage) :: k

使用类型错误存储任何类型ErrorMessage类型非常简单:

data ErrorMessage = Text     Symbol
                  | ShowType t
                  | ErrorMessage :<>: ErrorMessage
                  | ErrorMessage :$$: ErrorMessage

(:<>:)构造函数水平连接两个错误消息; (:$$:)构造函数将它们垂直连接起来。另外两个构造者按照他们的意思行事。

因此,在您的示例中,您可以使用TypeError填写最后一个案例;例如,

type family Testf a where
  Testf Char   = IO ()
  Testf String = IO ()
  Testf a      = TypeError (    Text "‘Testf’ didn't match"
                           :$$: Text "when applied to the type ‘"
                                :<>: ShowType a :<>: Text "’")

然后,尝试在pure ()类型中使用Testf Int将失败并显示错误

....hs:19:12: error: …
    • ‘Testf’ didn't match
      when applied to the type ‘Int’
    • In the expression: pure ()
      In an equation for ‘testfInt’: testfInt = pure ()
Compilation failed.

请注意,在定义

testfInt :: Testf Int
testfInt = pure ()

正确破坏,定义

testfInt :: Testf Int
testfInt = undefined

(或与testfInt = testfInt类似)工作正常。

这是一个完整的示例源文件:

{-# LANGUAGE UndecidableInstances, TypeFamilies, DataKinds, TypeOperators #-}

import GHC.TypeLits

type family Testf a where
  Testf Char   = IO ()
  Testf String = IO ()
  Testf a      = TypeError (    Text "‘Testf’ didn't match"
                           :$$: Text "when applied to the type ‘"
                                :<>: ShowType a :<>: Text "’")

testfChar :: Testf Char
testfChar = putStrLn "Testf Char"

testfString :: Testf Char
testfString = putStrLn "Testf String"

-- Error here!
testfInt :: Testf Int
testfInt = putStrLn "Int"