不透明类型是 Elm 中的一种封装技术。
有人也称之为信息隐藏,或隐藏实现细节。
它们还常被用来强制执行验证,采用解析而非验证模式。
例如,你可以有一个EmailAddress不透明类型,它只能包含有效的电子邮件地址(与之相对,String可以包含任何内容)。
自定义类型有一个或多个变体,编译器会根据变体的名称,自动生成一个函数来创建每个变体。 这是创建自定义类型的唯一方式。 编译器也允许对这些变体进行解构。
type Maybe a
= Nothing --> Variant implies a 'Nothing' function, with type 'Maybe a', and destructuring of 'Nothing'
| Just a --> Variant implies a 'Just' function, with type 'a -> Maybe a', and destructuring of 'Just _'
默认情况下,所有这些都只属于文件/模块内部,我们使用exposing关键字来选择要导出什么。
Maybe模块暴露了Maybe类型(这样其他模块就能使用已有的Maybe值),也暴露了Nothing和Just这两个变体(这样其他模块就能创建和解构Maybe值)。
它使用以下语法来做到这一点。
Maybe(..)这部分告诉 Elm 暴露所有变体,以及自定义类型本身的定义。
module Maybe exposing
( Maybe(..)
-- ...
)
要创建不透明类型,你只需选择不导出变体。
若要为Maybe类型做到这一点,你可以使用以下语法:
module Maybe exposing
( Maybe
-- ...
)
这样做时,你几乎总是需要提供另一种创建和获取变体的方式,即暴露自定义函数。 这些自定义函数让你在创建和获取变体时应用一些逻辑。 这隐藏了模块的内部实现细节,也意味着只要暴露的函数保持不变,你可以随意修改实现细节。 它还让你在创建和使用该类型时能够维护不变量。
不透明类型最常见的用途大概就是强制执行验证,采用解析而非验证模式。
在这种模式下,你暴露一个函数,它接收一个结构化程度或类型安全性低于你期望的输入,比如String,然后把它解析成结构更强的类型,比如EmailAddress,要么返回该类型,要么返回一个错误(通常是Maybe或Result)。
例如,在下面的代码中,外部模块只能通过parseEmailAddress函数创建EmailAddress类型,因此当你使用EmailAddress类型时,你就能确定它是有效的。
module EmailAddress exposing ( EmailAddress, parseEmailAddress, getEmailAddress )
-- The `EmailAddress` custom type has one variant, also called `EmailAddress`
-- When a custom type has one variant, it is idiomatic to use the same name for both.
type EmailAddress =
EmailAddress String
-- create an EmailAddress variant
parseEmailAddress: String -> Maybe EmailAddress
parseEmailAddress candidateEmailAddress =
if isValidEmailAddress candidateEmailAddress then
Just (EmailAddress candidateEmailAddress)
else
Nothing
-- get an EmailAddress variant
getEmailAddress: EmailAddress -> String
getEmailAddress ( EmailAddress emailAdress ) =
emailAdress
isValidEmailAddress: String -> bool
-- ...