模式匹配幻像类型

Edd*_*ang 7 haskell phantom-types data-kinds

我正在尝试实现一个CurrencyQty类型,它类似于在编译时标记的数字:

data Currency = Usd | Eur | Gbp

data CurrencyQty (a :: Currency) = CurrencyQty Double deriving (Num)
Run Code Online (Sandbox Code Playgroud)

现在我想实现一个动态查找汇率的通用转换函数.假设我有一些功能

currentExchangeRate :: Currency -> Currency -> IO Double
Run Code Online (Sandbox Code Playgroud)

我想写

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
Run Code Online (Sandbox Code Playgroud)

或者不知何故

inUsd :: CurrencyQty a -> IO (CurrencyQty Usd)
inUsd (CurrencyQty a x) = fmap (*x) $ currentExchangeRate Usd a
Run Code Online (Sandbox Code Playgroud)

我正在使用的语法显然是无效的haskell ...有没有办法实现这一目标?

chi*_*chi 5

你不能为此使用幻像。幻像类型在运行时消失,您的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
Run Code Online (Sandbox Code Playgroud)

让我补充一下,你的代码没问题。如果 Haskell 具有完全依赖类型,则可以通过细微调整来使用您的代码,并避免额外的单例类型。然而,目前 Haskell 无法避免这种情况,还需要付出更多的努力。