使用新型別改善型別安全性

別名型別減少了樣板並提高了可讀性,但它不比別名型別本身更安全。考慮以下:

type alias Email = String

type alias Name = String

someEmail = "holmes@private.com"

someName = "Benedict"

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

使用上面的程式碼,我們可以編寫 sendEmail someName,它會編譯,即使它真的不應該,因為儘管名稱和電子郵件都是 Strings,但它們完全不同。

通過建立一個新**型別,**我們可以在型別級別上真正區分一個 String 和另一個 String 。這是一個將 Email 重寫為 type 而不是 type 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-wrapped Email 以確保我們只返回有效的地址。雖然我們可以通過編寫 EmailAddress "somestring" 直接構造 Email 來回避驗證檢查,如果我們的模組宣告沒有暴露 EmailAddress 建構函式,如此處所示

module Email exposing (Email, create, send)

那麼沒有其他模組可以訪問 EmailAddress 建構函式,儘管他們仍然可以在註釋中使用 Email 型別。在此模組之外構建新 Email唯一方法是使用它提供的 create 函式,該函式確保它首先只返回有效的電子郵件地址。因此,此 API 通過其型別安全性自動引導使用者沿著正確的路徑:send 僅適用於由執行驗證的 create 構造的值,並且因為它返回 Maybe Email 而強制處理無效的電子郵件。

如果你想匯出 Email 建構函式,你可以寫

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

現在任何匯入 Email 的檔案也可以匯入其建構函式。在這種情況下,這樣做將允許使用者迴避驗證和無效的電子郵件,但你並不總是構建這樣的 API,因此匯出建構函式可能很有用。對於具有多個建構函式的型別,你可能還只想匯出其中的一些建構函式。