У меня есть тип данных F
со специальным случаем для Int
:
{-# LANGUAGE GADTs, RankNTypes #-}
data F a where
FGen :: a -> F a
FInt :: F Int
Без раскрытия деталей этого типа данных вызывающим абонентам - реальный тип данных более сложный, содержащий внутренние детали реализации - я хочу предоставить API для его использования:
transform :: (a -> b) -> b -> F a -> b
transform f i (FGen v) = f v
transform f i FInt = i
Если я назову transform
на a F Int
, очевидно, что оба первых двух аргумента важны:
transformInt :: F Int -> Int
transformInt = transform (+1) 5
Но если я назову его на F Char
, второй аргумент не нужен, поскольку значение не может быть FInt
:
transformChar :: F Char -> Char
transformChar = transform id (error "unreachable code")
Есть ли способ выразить это в типе transform
?
Я пробовал
transform :: (a -> b) -> (a ~ Int => b) -> F a -> b
transform f i (FGen v) = f v
transform f i FInt = i
но затем transformChar
не компилируется с
Couldn't match type ‘Char’ with ‘Int’
Inaccessible code in
a type expected by the context: (Char ~ Int) => Char
In the second argument of ‘transform’, namely
‘(error "unreachable code")’
In the expression: transform id (error "unreachable code")
In an equation for ‘transformChar’:
transformChar = transform id (error "unreachable code")
и в любом случае мне бы хотелось, чтобы значение absurd
могло использоваться вместо ошибки, чтобы правильно выразить, что компилятор должен иметь возможность доказать, что код никогда не будет использоваться.