检索幻像类型的隐藏类型

Retrieve hidden type of a phantom type

我用 Haskell 声明了一个像这样的幻像类型。

newtype Length (a::UnitLength) b = Length b deriving (Eq,Show)
data UnitLength = Meter
                | KiloMeter
                | Miles
                deriving (Eq,Show)

现在,我想写一些函数来使用这个类型。但是我没有碰巧看到和使用隐藏类型。

是否可以检索幻像类型Length的隐藏类型a来进行测试,模式匹配,....?

如果你想要你使用的虚拟类型的运行时表示,你必须使用我们所说的单例。 UnitLength 中的每个构造函数都有一个构造函数,它们的类型准确地说明了我们正在考虑的构造函数:

data SUnitLength (a :: UnitLength) where
  SMeter     :: SUnitLength Meter
  SKiloMeter :: SUnitLength KiloMeter
  SMiles     :: SUnitLength Miles

现在你已经有了这个,你可以写一个显示函数,根据虚参数选择正确的单位缩写:

display :: Show b => SUnitLength a -> Length a b -> String
display sa l = show (payload l) ++
  case sa of
    SKiloMeter -> "km"
    _          -> "m"

现在,这并不真正符合您的需求:参数 a 可以在类型 Length a b 中使用,但我们仍然需要手工制作 witness。这很烦人。避免这个问题的一种方法是定义一个类型 class 来为我们完成这项工作。 CUnitLength a 告诉我们,提供类型 Length a b 的值,我们可以获得 a 具有的形状的见证 SUnitLength a

class CUnitLength (a :: UnitLength) where
  getUnit :: Length a b -> SUnitLength a

我们很容易为各种UnitLength构造函数编写CUnitLength的实例:getUnit甚至可以忽略它的参数!

instance CUnitLength Meter where
  getUnit _ = SMeter

instance CUnitLength KiloMeter where
  getUnit _ = SKiloMeter

instance CUnitLength Miles where
  getUnit _ = SMiles

那么为什么要为 getUnit 的论点烦恼呢?好吧,如果我们删除它,getUnit 需要以某种方式神奇地猜测它应该描述哪个 a。有时可以根据调用站点的预期类型推断出 ̀a 但有时不是。使用 Length a b 参数可以保证所有调用都是明确的。无论如何,我们总是可以恢复更简单的 getUnit':

getUnit' :: CUnitLength a => SUnitLength a
getUnit' = getUnit (undefined :: Length a ())

这导致我们找到最后一个定义 display',它与 display 具有相同的作用,但不需要额外的参数:

display' :: (CUnitLength a, Show b) => Length a b -> String
display' = display getUnit'

我已将所有内容(包括 LANGUAGE 扩展,以及 payload 的定义以从 Length a b 中提取 b)放在 self-contained gist 中,以备不时之需玩代码。