Создание (получение) экземпляра значения из фантомного типа

Я использую 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 заставляет его объединяться.

Другие вопросы по тегам