目的
- 必須フィールド「a」「b」
- 任意フィールド「c」「d」
を持つレコードMyRecordを作りたい!
方法
import Prim.Row as Row
import Unsafe.Coerce (unsafeCoerce)
data MyRecord
type MyRecordRequired opt = { a :: Int, b :: Int | opt }
type MyRecordOptional = ( c :: Int, d :: Int )
type MakePartialRecord (req :: # Type -> Type) (opt :: # Type) (rec :: Type)
= forall options trash
. Row.Union options trash opt
=> req options
-> rec
makeMyRecord :: MakePartialRecord MyRecordRequired MyRecordOptional MyRecord
makeMyRecord = unsafeCoerce
名前の通りですが、
- MyRecordが目的のレコード
- MyRecordRequiredがMyRecordの必須フィールド
- MyRecordOptionalがMyRecordの任意フィールド
です。
MakePartialRecord型シノニムを関数っぽく使えます。
検証
以下は全て正しい形式です。
test1 :: MyRecord
test1 = makeMyRecord { a: 1, b: 1 }
test2 :: MyRecord
test2 = makeMyRecord { a: 1, b: 1, c: 1 }
test3 :: MyRecord
test3 = makeMyRecord { a: 1, b: 1, d: 1 }
test4 :: MyRecord
test4 = makeMyRecord { a: 1, b: 1, c: 1, d: 1 }
以下は全てエラーになります。
-- aとbが足りない!
test5 :: MyRecord
test5 = makeMyRecord {}
-- bが足りない!
test6 :: MyRecord
test6 = makeMyRecord { a: 1 }
-- aが足りない!
test7 :: MyRecord
test7 = makeMyRecord { b: 1 }
-- aとbが足りない!
test8 :: MyRecord
test8 = makeMyRecord { c: 1, d: 1 }
用途
使い道はたくさんあると思いますが、例えば、
「『名前』と『年齢』は必須だけど『住所』は無くてもいい!」
という場面を想定すると、以下のような定義を用意して、
data UserInfo
type UserInfoRequired opt
= { name :: String
, age :: Int
| opt
}
type UserInfoOptional
= ( address :: String
)
makeUserInfo :: MakePartialRecord UserInfoRequired UserInfoOptional UserInfo
makeUserInfo = unsafeCoerce
以下のような目的のユーザ情報を作成することができます。
userInfo1 :: UserInfo
userInfo1 = makeUserInfo { name: "matoruru", age: 3 }
userInfo2 :: UserInfo
userInfo2 = makeUserInfo { name: "neko", age: 2, address: "Abashiri" }