2015-12-21 56 views
0

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

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

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

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

+2

使用类型进行任何计算的最简单方法是使用类型类。你想写什么函数? – user2407038

回答

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的值,我们可以得到形状为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中,以防您想要使用该代码。

+0

“SUnitLength”是一个GADT,因此关闭,这一事实似乎有点尴尬。此外,我不明白你为什么想在单元上进行模式匹配。像数据家族这样的东西会更合适吗? – dfeuer

+0

'UnitLength'已经关闭了,所以除了提供的三个单元外,不可能使用'Length'。 – gallais

+0

是的,但不能换成'*'或其他东西? – dfeuer