Тип совпадения фантомного типа - PullRequest
0 голосов
/ 12 сентября 2018

Я пытаюсь реализовать тип CurrencyQty, который действует как число, помеченное во время компиляции:

data Currency = Usd | Eur | Gbp

data CurrencyQty (a :: Currency) = CurrencyQty Double deriving (Num)

А теперь я хочу реализовать универсальную функцию преобразования, которая динамически ищет обменные курсы.Предположим, у меня есть какая-то функция

currentExchangeRate :: Currency -> Currency -> IO Double

Я хочу написать

inUsd :: CurrencyQty a -> IO (CurrencyQty Usd)
inUsd (CurrencyQty Usd x) = return x
inUsd (CurrencyQty Eur x) = fmap (*x) $ currentExchangeRate Usd Eur
inUsd (CurrencyQty Gbp x) = fmap (*x) $ currentExchangeRate Usd Gbp

Или, может быть, как-то

inUsd :: CurrencyQty a -> IO (CurrencyQty Usd)
inUsd (CurrencyQty a x) = fmap (*x) $ currentExchangeRate Usd a

Синтаксис, который я использую, очевидно, недействителенhaskell ... есть ли способ сделать это?

1 Ответ

0 голосов
/ 12 сентября 2018

Вы не можете использовать фантом для этого.Фантомные типы исчезают во время выполнения, и вам нужна некоторая информация о времени выполнения для вашей inUsd функции.

Обычный подход заключается в использовании GADT и синглтон-типов.

-- the "index" type
data Currency = Usd | Eur | Gbp

-- the "singleton" type
-- If you want to autogenerate this, check out the singletons
-- package on Hackage
data SCurrency (a :: Currency) where
   SUsd :: Scurrency Usd
   SEur :: Scurrency Eur
   SGbp :: Scurrency Gbp

-- the "indexed" type
data CurrencyQty (a :: Currency) where
  CurrencyQty :: SCurrency a -> Double -> CurrencyQty a

instance Num (CurrencyQty a) where
   ... -- you have to manually write this, I guess?

inUsd :: CurrencyQty a -> IO (CurrencyQty Usd)
inUsd (CurrencyQty SUsd x) = return x
inUsd (CurrencyQty SEur x) = fmap (*x) $ currentExchangeRate Usd Eur
inUsd (CurrencyQty SGbp x) = fmap (*x) $ currentExchangeRate Usd Gbp

Позвольте мне добавить, что вашиКод в порядке.Если бы Haskell имел полностью зависимые типы, можно было бы использовать ваш код с небольшими изменениями, избегая дополнительного типа синглтона.Однако в настоящее время Haskell не может этого избежать, и требуются дополнительные усилия.

...