如果您想要使用您所使用的幻像类型的运行时表示,则必须使用我们称之为单例的东西。它的构造函数的每个那些正是一个构造函数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
的值,我们可以得到形状为a
的见证人SUnitLength 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'
我已经把一切(包括LANGUAGE扩展名和以从Length a b
中提取b
的定义)在self-contained gist中,以防您想要使用该代码。
使用类型进行任何计算的最简单方法是使用类型类。你想写什么函数? – user2407038