こんにちは、HolySheep AI 技術ブログ編集部の田中です。私は2024年からLLM APIのコスト最適化_nullablenull_null_null_null_null_null_null_null_nullablenull_null_null_null_null_null_null_null_null_null_null_null_null_null_null_nullablenull_null_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_null_null_null_nullablenull_null_null_null_null_nullablenull_flatMap { case _: => ... } .orElse
4. Future[Try[...]] からの変換
Scala標準のFuture と Try を組み合わせたケースからの変換は、最も複雑なパターンの一つです。
import scala.concurrent.Future
import scala.util.Try
// Future[Try[...]] を OptionT に変換
def convertFutureTryToOptionT[F[_]: Functor](
futureTry: Future[Try[A]]
)(using Concurrent): OptionT[F, A] = {
OptionT {
Concurrent[F].async_/*.attempt*/.map(futureTry) {
case scala.util.Success(scala.util.Success(a)) => Some(a)
case _ => None
}
}
}
5. практические примеры
実際のアプリケーションからの具体的な変換例を見てみましょう。5.1 データベースクエリ結果の変換
import cats.data.OptionT
class UserRepository[F[_]: MonadCancelThrow](repo: UserRepo) {
def findUserById(id: UserId): F[Option[User]] = repo.find(id)
def findActiveUserById(id: UserId): OptionT[F, User] =
OptionT(findUserById(id)).filter(_.isActive)
def findUserEmail(id: UserId): OptionT[F, Email] =
findActiveUserById(id).map(_.email)
}
5.2 HTTPリクエスト結果の変換
import cats.data.OptionT
import cats.effect.IO
case class UserContext(userId: UserId, email: Email)
def getCurrentUser(
request: Request[IO]
): IO[Option[UserContext]] = {
for {
userId <- extractUserId(request)
user <- userRepo.findById(userId)
} yield user.map(u => UserContext(u.id, u.email))
}
// OptionTを使った链式处理
def getUserEmail(request: Request[IO]): IO[Option[Email]] =
extractUserId(request)
.toOptionT // IO[Option[...]] => OptionT[IO, ...]
.flatMap(userRepo.findById)
.map(_.email)
.value
6. cats.data パッケージの他のデータ型との組み合わせ
6.1 EitherTとの組み合わせ
import cats.data.{EitherT, OptionT}
import cats.instances.option._
type ResultF[T] = EitherT[IO, Error, T]
type RichResultF[T] = OptionT[ResultF, T]
def findUser(id: UserId): RichResultF[User] = {
OptionT {
EitherT {
IO {
if (id.value.startsWith("admin_"))
Right(Some(User.admin(id)))
else
Right(Some(User.regular(id)))
}
}.attempt
.map(_.toEither)
}
}
6.2 ValidatedNelとの組み合わせ
import cats.data.{OptionT, ValidatedNec}
def validateAndFind(
userId: UserId,
email: Email
): IO[Option[ValidatedNec[ValidationError, User]]] = {
val validation =
(validateId(userId), validateEmail(email)).mapN {
case (vid, vemail) => User(vid, vemail)
}
userRepo.findById(userId).map(_.map(v => validation.map(_ => v).getOrElse(v)))
}
7. 性能に関する考慮事项
OptionT への変換には性能上の注意点があります。
| 変換元 | 変換先 | 性能影響 | 推奨ケース |
|--------|--------|----------|------------|
| Option | OptionT[F, A] | 最小 | 単純な Optional 値の包装 |
| F[Option[A]] | OptionT[F, A] | 中程度 | 既存 API との互換性維持 |
| Future[Option[A]] | OptionT[F, A] | 高い | ブロッキング IO の回避が必要な場合 |
| Either[E, A] | OptionT[F, A] | 最小 | エラー情報の損失に注意 |
8. cats-mtl との統合
cats-mtl ライブラリを使用すると、より強力な変換機能を利用できます。
import cats.mtl.Tell
import cats.mtl.instances.option._
// ログ収集付きの OptionT 変換
def withLogging[F[_]: Monad, A](
action: F[Option[A]],
logger: Tell[F, List[String]]
): OptionT[F, A] = {
val optionT = OptionT(action)
OptionT {
optionT.value.flatTap { result =>
logger.tell(List(s"OptionT result: $result"))
}
}
}
9. まとめ
Cats Effect環境でのOptionT への変換は、以下のポイントを押さえることで安全に実装できます:
1. **変換元の_effect の特性_を把握する**:MonadError、Concurrent、Async など、必要な型クラスを確認する
2. **エラー処理戦略を明確にする**:例外を例外として扱うか、Option として扱うか
3. **链式调用の النهايةで value を呼ぶ**:常に OptionT[F, A] から F[Option[A]] への復元を忘れない
4. **性能テストを行う**:高負荷環境では変換のオーバーヘッドが問題になる場合がある
これらのパターンを適用することで、既存の Scala コードから Cats Effect への移行を段階的に進めることができます。
// 最终的なヘルパー関数の例
object OptionTConverters {
implicit class FOptionOps[F[_], A](val fa: F[Option[A]]) extends AnyVal {
def toOptionT: OptionT[F, A] = OptionT(fa)
}
implicit class FutureOptionOps[A](val fo: Future[Option[A]]) {
def toOptionT[F[_]: Async]: OptionT[F, A] =
OptionT.liftF {
import scala.concurrent.ExecutionContext.Implicits.global
Async[F].fromFuture(fo.pure)
}.flatMap(_.fold[OptionT[F, A]](OptionT.none)(a => OptionT.some(a)))
}
}
実際のプロジェクトでは、これらの変換パターンを統一的な Conversions オブジェクトにまとめ、チーム内で再利用可能な形にすると効率的です。
---
付録: Cats Effect 3 での変更点
Cats Effect 3 では、いくつかのAPIが変更されています:// CE3 での新しい記法
import cats.effect.{IO, Async}
def convertUsingCE3[A](fo: Future[Option[A]]): IO[Option[A]] = {
import scala.concurrent.ExecutionContext.global
Async[IO].futureLift(fo)
}
Cats Effect 3では、ConcurrentEffect が Concurrent と Async に分割され、より柔軟な変換が可能になっています。
---
*筆者: HolySheep AI 技術チーム - Cats Effect と関数型プログラミングを愛するエンジニア集団*