Создание (получение) экземпляра значения из фантомного типа
Я использую GADT для создания системы базовых измерений (как в физических измерениях) для валют. Размеры (например, USD, USD/EUR, EUR/USD) представлены в виде фантомных типов. Я хотел бы иметь возможность печатать количество валюты в стиле, например, "10.3USD" или "0EUR" и курс, например, "10.3USD/EUR", используя Show. Я не совсем уверен, как объяснить мою проблему, поэтому я приведу пример того, как я пытался ее решить:
{-# LANGUAGE GADTs #-}
class (Show a) => Currency a where unphantom :: a
data USD = USD deriving Show
data EUR = EUR deriving Show
instance Currency USD where unphantom = USD
instance Currency EUR where unphantom = EUR
data Amount a where
Amount :: Currency a => Float -> Amount a
instance Show (Amount a) where
show (Amount x) = show x ++ show (unphantom :: a)
data Rate a b where
Rate :: (Currency a, Currency b) => Float -> Rate a b
-- ...
С этим кодом я получаю ошибку
$ ghc example.hs
[1 of 1] Compiling Main ( example.hs, example.o )
example.hs:14:37:
Could not deduce (Currency a1) arising from a use of `unphantom'
from the context (Currency a)
bound by a pattern with constructor
Amount :: forall a. Currency a => Float -> Amount a,
in an equation for `show'
at example.hs:14:9-16
Possible fix:
add (Currency a1) to the context of
an expression type signature: a1
or the data constructor `Amount'
or the instance declaration
In the first argument of `show', namely `(unphantom :: a)'
In the second argument of `(++)', namely `show (unphantom :: a)'
In the expression: show x ++ show (unphantom :: a)
Я должен сказать, что я не понимаю, почему компилятор в этом случае говорит о a1
типа, когда я указал a
,
Конечно, я хочу избежать представления измерений вне системы типов haskell, поскольку это добавляет мне дополнительный шаблонный код и, насколько я могу сказать, теоретически не нужно (т.е. компилятор должен иметь достаточно информации, чтобы определить, как отображать Amount или a Оценить во время компиляции) (и добавляет немного накладных расходов во время выполнения).
1 ответ
Использование ScopedTypeVariables
и ваш код компилируется как есть.
В частности, без ScopedTypeVariables
когда ты пишешь
instance Show (Amount a) where
show (Amount x) = show x ++ show (unphantom :: a)
a
в unphantom :: a
свеж и не сделан, чтобы объединяться с a
в instance Show (Amount a) where
, Включение ScopedTypeVariables
заставляет его объединяться.