При проектировании встроенных языков доменной специфики на Haskell полезно иметь что-то вроде error на уровне типов. Таким образом, разработчик EDSL может отобразить ошибку типа, специфичную для DSL, а не стандартную ошибку типа GHC.
Например, рассмотрим класс типов, который не предназначен для использования с функциями, но пользователь случайно использовал его для типа функции, возможно, потому что пропустил аргумент какой-то функции. Тогда вместо стандартного сообщения GHC об отсутствии экземпляра было бы лучше вывести более дружелюбное сообщение, специфичное для EDSL. Аналогично, редукция функции на уровне типов может застрять из-за ошибки, в этом случае было бы неплохо сообщить о специфичной для EDSL ошибке, а не о общей ошибке неопределённого типа.
Для решения этой проблемы GHC предоставляет единственную функцию на уровне типов,
type family TypeError (msg :: ErrorMessage) :: k
вместе с небольшим языком на уровне типов (через DataKinds) для построения сообщений об ошибках с красивой печатью,
-- 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
в модуле GHC.TypeLits.
Например, мы можем использовать этот интерфейс, чтобы предоставить более полезное сообщение об ошибке для приложений show для неполных функций, например,
{-# 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
Что выведет следующую ошибку времени компиляции,
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