generated at
newtypeとsmart constructorのmodule


以下の組み合わせのmoduleを設計する
型は公開
値コンストラクタは非公開
公開
unwrapする関数
公開


ref
Password.hs
module Password ( Password -- ←これは型 , unPassword , mkPassword ) where import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString newtype Password = Password ByteString unPassword :: Password -> ByteString unPassword (Password password) = password mkPassword :: ByteString -> Maybe Password mkPassword pwd | ByteString.null pwd = Nothing | otherwise = Just (Password pwd)
Password の値constructorは公開せずに、 mkPassword のみを公開している
Password型を生成するために、 mkPassword を使うように強制できる
mkPassword では、validationを行い、不正なデータでPasswordを定義させない
ここでは空文字のPasswordを指定させないようにしている
Password の値consturctorを公開していないので、値を取り出すために unPassword 関数が必要


2つのアプローチ ref
「Usernameは空文字ではいけない」という仕様があって、それをどう表現するか
A.hs
newtype Username = Username String mkUsername :: String -> Maybe Username mkUsername "" = Nothing mkUsername s = Just (Username s)
B.hs
newtype Username = Username NonEmptyString
Aのほうは、Stringでnewtypeして、空文字validationするために、smart constructorを使っている
Bの方は、そもそも型レベルで空文字を受け付けない
今後の仕様の変わりようも考えると、Aの方が柔軟な気もする
validationに「記号から始まってはいけない」とか「3文字以上」とかの要件が加わる可能性はある
Cとして、NonEmptySringにsmart consturctorを使う、というのもあり得る
これが良いと思うmrsekut



mkする場所やunwrapする場所は、外部との境界値になる



unwrapさせない工夫
unwrapするのではなく、unwrapしたものに適用する関数を渡すようにする
こうではなく
before.fs
address |> EmailAddress.value |> printfn "the value is %s"
こうする
after.fs
address |> EmailAddress.apply (printfn "the value is %s")
こうすることで、unwrapされた値を持ち回ることを防ぐことができる
unwrapした値を取り出せないので
でも id を適用すれば取り出せるかmrsekut



ここに出てきた


簡潔でわかりやすい
ここでは、返り値を Either ソレ String にしようというふうに書かれている
smart constructorの話ではないけど

String50 String100 などを個別に作りつつも互換性をもたせる方法