Haskell是否能够指示类型系列匹配错误?例如,使用封闭类型族:
type family Testf a where
Testf Char = IO ()
Testf String = IO ()
Testf Int
的类型只是Testf Int
。编译器不会生成任何类型的错误。如果没有匹配,是否可以让它生成一个?
答案 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"