エフェクトの文書化
LT;DR
エフェクトを用いて、関数が主な出力以外に行うことを説明する エラーが起こり得るケース: Result
I/O 処理を行うケース: Await
hr.icon
エラーを返すか?(Result エフェクト)
I/O 処理をするか?(Async エフェクト)
検証のステップ
2 つの依存関係
CheckProduceCodeExists (製品コードの存在チェック)
こちらはエフェクトを用いていない(DMMF p.33) type CheckProductCodeExists = ProductCode -> bool
CheckAddressExists (アドレスの存在チェック)
こちらは外部サービスを用いているとする(DMMF p.33) Async エフェクトと Result エフェクトが必要
これらは一緒に使われることが多いため、AsyncResultという型にまとめることが一般的
code:fsharp
type AsyncResult<'success, 'failure> = Async<Result<'success, 'failure>>
code:fsharp
type CheckAddressExists =
UnvalidatedAddress -> AsyncResult<CheckedAddress, AddressValidationError>
I/O 処理をしていて、失敗する可能性があることが 型シグネチャ から明らかになる 自律性 を持たせるために、アドレス検証サービスのローカルバージョンを作るべきか? 今回のようにサービスを利用する場合
そのサービスを信頼するか、問題が発生した場合の準備が必要
Result 同様、Async もすべてのコードに伝染する
code:fsharp
type ValidateOrder =
CheckProductCodeExists // 依存関係
-> CheckAddressExists // 依存関係
-> UnvalidatedOrder // 入力
-> AsyncResult<ValidatedOrder, ValidationError> // 出力
価格設定のステップ
1 つの依存関係
GetProductPrice(製品価格の取得)
エフェクトを用いていない(p.27)
PriceOrder(注文の価格計算)のステップがエラーを返す可能性がある
e.g. 価格が間違っていて、AmountToBill(請求額の合計)が非常に大きく(or マイナス)になる
code:fsharp
type PricingError = PricingError of string
type PriceOrder =
GetProductPrice // 依存関係
-> ValidatedOrder // 入力
-> Result<PricedOrder, PricingError> // 出力
注文確認のステップ
2 つの依存関係
CreateOrderAcknowledgmentLetter(注文確認書の作成)
エフェクトを用いていない(p.34)
SendOrderAcknowledgment(注文確認の送信)
メールで送信する(p.35)のでエフェクトを用いている
Async エフェクトを用いる
type SendOrderAcknowledgment = OrderAcknowledgment -> Async<SendResult>
エラーについては気にしない
仕様次第だが、メールアドレスが完全に正しいとしても、以下のようなケースなら失敗するので考慮したほうが良い気がする radish-miyazaki.icon
検証 〜 送信までの間にメールアドレスが失効した
利用している外部のメールサーバが停止している
Async は親関数にも伝搬する
code:fsharp
type Acknowledgment =
CreateOrderAcknowledgmentLetter // 依存関係
-> SendOrderAcknowledgment // 依存関係
-> PricedOrder // 入力
-> Async<OrderAcknowledgmentSent option> // 出力