Elm使用新类型提高类型安全性

示例

别名类型可以减少样板并提高可读性,但是它比别名类型本身更安全。考虑以下:

type alias Email = String

type alias Name = String

someEmail = "holmes@private.com"

someName = "Benedict"

sendEmail : Email -> Cmd msg
sendEmail email = ...

使用上面的代码,我们可以编写sendEmail someName,即使它实际上不应该编译,也可以编译,因为尽管名称和电子邮件都是Strings,但它们是完全不同的。

通过创建新的类型,我们可以在类型级别上真正区分String另一个。这是一个改写为而不是的示例:StringEmailtypetype alias

module Email exposing (Email, create, send)

type Email = EmailAddress String

isValid : String -> Bool
isValid email = 
  -- ...validation logic

create : String -> Maybe Email
create email =
    if isValid email then
        Just (EmailAddress email)
    else
        Nothing

send : Email -> Cmd msg
send (EmailAddress email) = ...

我们的isValid函数会执行某些操作来确定字符串是否为有效的电子邮件地址。该create函数检查给定String的电子邮件是否有效,并返回Maybe-wrappedEmail以确保我们仅返回经过验证的地址。如果我们的模块声明未公开构造函数,则Email可以通过直接编写来避开验证检查,如此处所示EmailAddress "somestring"EmailAddress

module Email exposing (Email, create, send)

那么其他模块将无法访问EmailAddress构造函数,尽管它们仍可以Email在注释中使用该类型。在此模块之外构建新的唯一方法Email是使用create它提供的功能,并且该功能可确保首先仅返回有效的电子邮件地址。因此,此API会通过其类型安全性自动将用户引导至正确的路径:send仅适用于由构造的值create,该值执行验证,并由于返回a来强制处理无效电子邮件Maybe Email。

如果要导出Email构造函数,可以编写

module Email exposing (Email(EmailAddress), create, send)

现在,任何Email导入的文件也可以导入其构造函数。在这种情况下,这样做会使用户避开验证和send无效的电子邮件,但是您并不总是构建这样的API,因此导出构造函数可能会很有用。对于具有多个构造函数的类型,您可能还只希望导出其中一些构造函数。