カスタムコンパイルエラー
HaskellでEDSL(Embedded Domain Specific Languages)を設計する時,型レベルでerrorのようなものを持つことは有用である.こうすれば,EDSLの設計者は,標準のGHCのエラーの代わりにDSL固有の型エラーを表示できる.
例えば,関数に使用されることを意図していない型クラスが,おそらくユーザがある関数の引数を書き忘れたせいで,誤って関数型で使われた例を考えてみよう.その場合,インスタンスがないという標準的なGHCのメッセージを得る代わりに,EDSLに固有のよりわかりやすいエラーメッセージを表示するほうがよいだろう.同様に,型レベル関数の簡約はエラーによって動かなくなる可能性がある.その場合は,あいまいな型に関する一般的なエラーではなく,EDSL固有のエラーを報告するほうが良いだろう.
これを解決するために,一つの型レベル関数と,プリティプリントされたエラーメッセージを作成するための(DataKindを介した)小さな型レベル言語をGHCは提供する.
code:cce1.hs
type family TypeError (msg :: ErrorMessage) :: k
code:cce2.hs
-- ErrorMessage is intended to be used as a kind
data ErrorMessage =
Text Symbol -- Show this text as is
| forall t. ShowType t -- Pretty print a type
| ErrorMessage :<>: ErrorMessage -- Put two chunks of error message next to each other
| ErrorMessage :$$: ErrorMessage -- Put two chunks of error message above each other
例えば,以下のような完全適用されていない関数に対するshowの適用に対して,より有用なエラーメッセージを提供するためにこのインターフェイスを使うことができる.
code:cce3.hs
{-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} import GHC.TypeLits
instance TypeError (Text "Cannot 'Show' functions." :$$:
Text "Perhaps there is a missing argument?")
=> Show (a -> b) where
showsPrec = error "unreachable"
main = print negate
これにより,次のコンパイル時エラーが発生する.
code:cce4
Test.hs:12:8: error:
• Cannot 'Show' functions.
Perhaps there is a missing argument?
• In the expression: print negate
In an equation for ‘main’: main = print negate