不透明型は、Elmにおけるカプセル化の手法です。
情報隠蔽、あるいは実装の詳細の隠蔽と呼ぶ人もいます。
また、Parse, dont validateパターンを使って検証を強制するのにもよく使われます。
たとえば、有効なメールアドレスしか保持できないEmailAddressという不透明型を作ることができます(どんな文字列でも入るStringとは対照的です)。
カスタム型には1つ以上のバリアントがあり、コンパイラーはバリアントの名前を使って、それぞれのバリアントを作る関数を自動的に生成します。 カスタム型を作れるのは、これが唯一の方法です。 コンパイラーは、これらのバリアントを分解することも許しています。
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
-- ...
)
こうすると、ほとんどの場合、バリアントを作ったり取得したりする別の方法を、カスタム関数を公開することで用意する必要があります。 これらのカスタム関数を使うと、バリアントを作ったり取得したりするときに何らかのロジックを適用できます。 これにより、モジュール内部の実装の詳細が隠され、公開している関数が同じままであれば、実装の詳細をいくらでも変更できます。 また、型を作ったり使ったりするときに不変条件を守ることにも役立ちます。
不透明型の最も一般的な使い道は、Parse, dont validateパターンを使って検証を強制することでしょう。
このパターンでは、Stringのように、望むよりも構造や型安全性の少ない入力を取る関数を公開し、それをEmailAddressのようなより構造化された型へパースします。戻り値はその型か、エラー(ふつうはMaybeかResult)です。
たとえば、次のコードでは、外部のモジュールがEmailAddress型を作れるのはparseEmailAddress関数を使う場合だけなので、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
-- ...