2016-09-20 69 views
1

是否支持定义只能通过特定功能实例化的类型的语言或类型系统。绑定到特定功能的名义类型

一个示例可能是与函数CreateNonZeroInteger绑定的类型NonZeroInteger。任何外部代码都可以访问该类型,接收和返回此类型的值。但重要的是,只能通过调用函数CreateNonZeroInteger

NonZeroInteger应该名义上键入来创建该类型的值。具有相同结构的另一种类型的值不应与此类型相比或可铸造。

+5

我认为这通常是用隐藏数据构造函数的模块来完成的,并暴露了一个包装数据构造函数的函数。在这里看到最佳答案:http://stackoverflow.com/questions/39531032/using-types-to-force-correctness – zoran119

+0

在f#中,您可以使用访问修饰符,但它会很丑陋 –

+1

“*是否有语言或类型系统它支持定义只能通过特定函数实例化的类型。*“好吧,C++ for one;但我不知道任何基于_Hindley-Milner的类型系统完全支持它,这似乎是你实际要求的。 – ildjarn

回答

7

创建一个类型,定义一个函数来创建该类型的值,然后导出类型和函数,但不是而是该类型的数据构造函数。例如,在Haskell,给你的例子:

module My.Module.NonZeroInteger (NonZeroInteger, createNonZeroInteger) where 

newtype NonZeroInteger = NonZeroInteger Integer 
    deriving (Show, Eq, Ord) 

createNonZeroInteger :: Integer -> Maybe NonZeroInteger 
createNonZeroInteger 0 = Nothing 
createNonZeroInteger x = Just $ NonZeroInteger x 

My.Module.NonZeroInteger消费者将能够创建NonZeroInteger类型的值,但由于数据的构造不出口,他们将永远不会成为能够创造NonZeroInteger小号这是内部0

自定义构造函数,在这种情况下为createNonZeroInteger,传统上称为“smart constructor”

+0

我真的希望类型本身绑定到签名中的函数,而不是利用模块封装。但这真的很有帮助,并回答了这个问题。 –

2

我觉得值得在这里记录下来,你可以做到亚历克西斯金在F#中建议的完全一样的东西,通过使工会案例构造函数private

type NonZeroInteger = private NonZeroInteger of int 

let tryCreateNonZeroInteger = function 
    |0 -> None 
    |x -> Some <| NonZeroInteger x 

该类型本身仍然是可公开访问的,但它只能通过您提供的函数创建和分解。