我用Haskell声明了这样的幻像类型。
newtype Length (a::UnitLength) b = Length b deriving (Eq,Show)
data UnitLength = Meter
| KiloMeter
| Miles
deriving (Eq,Show)
现在,我想写一些函数来使用这种类型。但我没有碰巧看到并使用隐藏类型。
是否可以检索幻像类型a
的隐藏类型Length
以执行测试,模式匹配,....?
答案 0 :(得分:4)
如果您想要使用您使用的幻像类型的运行时表示,则必须使用我们称之为单例的方法。对于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
类型中可用,但我们仍然需要手工制作见证。那很烦人。避免此问题的一种方法是定义一个为我们工作的类型类。 CUnitLength a
告诉我们,如果提供Length a b
类型的值,我们就可以获得SUnitLength a
形状的见证a
。
class CUnitLength (a :: UnitLength) where
getUnit :: Length a b -> SUnitLength a
我们很容易为各种CUnitLength
构造函数编写UnitLength
的实例: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'
我已将payload
中的所有内容(包括LANGUAGE扩展名和b
的定义)从Length a b
中提取ObjectFactory
,以防您想玩用代码。