
Scala
イベント
該当するコンテンツが見つかりませんでした
マガジン
技術ブログ
みなさん、こんにちは。 いなりく です。 新年あけましておめでとうございます。みなさん Kiro ライフをいかがお過ごしでしょうか。 Kiro CLI 1.24.0 では、 大規模なドキュメントセットの段階的な読み込みを可能にする Skills 、 カスタム Diff ツール 、 18 言語に対応した組み込みコードインテリジェンス 、 リモート認証 、 web_fetch ツールの詳細な権限管理 、 長時間のセッションをスムーズに維持する会話 圧縮の詳細なコントールが導入されました。これらのアップデートが私の Kiro ライフを更に快適にしてくれたので、今回はこれらの追加された機能を深堀ってご紹介します。Kiro って何?という方は「 Kiroweeeeeeek in Japan 開催のお知らせ 」を読んでいただけると Kiro の全体像を掴んでいただけると思います。気になるアップデートのセクションおよび移行ガイドだけを読んでいただいても問題ありません。Kiro CLI の v.1.21.0 から v.1.23.0 までのアップデートに関しては「 Kiro CLI 新機能まとめ : v1.21.0 から v1.23.0 」をぜひお読み下さい。 アップデート1 : Skills による段階的なコンテキスト読み込み アップデート2 : カスタム Diff ツール アップデート 3 : AST パターンツールによる正確なリファクタリング アップデート 4 : 改善されたコードインテリジェンス アップデート 5 : 会話圧縮の詳細なコントロール アップデート 6 : web_fetch ツールの詳細な URL 権限管理 アップデート 7 : リモート認証 移行ガイド アップデート 1 : Skills による段階的なコンテキスト読み込み Skills は起動時にはメタデータ(名前と説明)のみが読み込まれ、エージェントが必要と判断したときにのみ完全なコンテンツが読み込まれます。これにより、コンテキストウィンドウを効率的に管理しながら、広範なドキュメントへのアクセスを提供できます。 Skills の仕組み 従来の Steering ファイルは、エージェント起動時にすべてのコンテンツをコンテキストウィンドウに読み込みます。これは小規模なファイルには適していますが、大規模なドキュメントセットではコンテキストウィンドウを圧迫してしまいます。 Skills は以下のアプローチを採用しています。 起動時 :名前と説明のみが読み込まれる 実行時 :エージェントが関連性を判断し、必要に応じて完全なコンテンツを読み込む 効率性 :使用されないドキュメントはコンテキストを消費しない Skills ファイルの作成 Skills ファイルには、YAML フロントマターで記述された説明的なメタデータが必要です。エージェントが完全なコンテンツを読み込むタイミングを確実に判断できるよう、具体的な説明を記述してください。 --- name: dynamodb-data-modeling description: DynamoDB データモデリングのベストプラクティスガイド。DynamoDB スキーマの設計または分析時に使用。 --- # DynamoDB データモデリング ## 概要 DynamoDB は NoSQL データベースで、適切なデータモデリングが重要です... ## パーティションキーの設計 パーティションキーは均等に分散する必要があります... ## ソートキーのパターン ソートキーを使用すると、効率的なクエリパターンが可能になります... Skills と Steering の使い分け Skills を使用する場合: 大規模なドキュメントセット(API リファレンス、アーキテクチャガイドなど) 特定のタスクでのみ必要な専門知識 コンテキストウィンドウの効率的な管理が必要な場合 複数のトピックに分かれた参照ドキュメント Steering を使用する場合: すべての会話で常に必要な小規模なファイル(README、設定ファイルなど) プロジェクトの基本情報やコンテキスト エージェントの動作を常に制御したいコーディング規約やスタイルガイド カスタムエージェント設定での Steering/Skills の使用 カスタムエージェントでは Skills/Steering ファイルは自動で読み込まれず、カスタムエージェント設定ファイルの resources フィールドで明示的に指定する必要があります。Glob パターンを使用すると、複数の SKill ファイルを一度に含めることができます。エージェントは各 Skills のメタデータを読み込み、会話の文脈に基づいて関連する Skill を自動的に読み込みます。 以下の例では README.md と Steering ファイル(coding-standards.md、project-rules.md)はカスタムエージェントで常に読み込まれ、Skills として、api-reference.md、architecture-guide.md、deployment-guide.md が必要なときだけ読み込まれます。 詳細については、 Skills リソースのドキュメント を参照してください。 { "resources": [ "file://README.md", "file://.kiro/steering/coding-standards.md", "file://.kiro/steering/project-rules.md", "skill://docs/api-reference.md", "skill://docs/architecture-guide.md", "skill://docs/deployment-guide.md" ] } アップデート 2 : カスタム Diff ツール Kiro がファイルの変更を提案する際、デフォルトでは組み込みの Diff ツールを使用して変更内容を表示します。1.24.0 では、外部の Diff ツールを設定できるようになり、シンタックスハイライト、サイドバイサイド表示、お気に入りの GUI ツールなど、好みの Diff 表示方法を選択できます。 設定方法 chat.diffTool 設定で、好みの Diff ツールを指定します。 kiro-cli settings chat.diffTool delta カスタム Diff ツール (delta を利用した場合) 組み込みの Diff には以下のコマンドで戻すことができます。 kiro-cli settings -d chat.diffTool 組み込み diff ツール ターミナルツール ターミナルで直接 Diff を表示するツールは、ワークフローを中断しません。 delta :Git ユーザー向けのシンタックスハイライトと行番号表示 difftastic :フォーマットの違いを無視する言語対応の構造的 Diff icdiff :素早いサイドバイサイドのカラー比較 diff-so-fancy :クリーンで人間が読みやすい出力 colordiff :シンプルなカラー表示の Diff bat :Git 統合を備えたシンタックスハイライト GUI ツール 変更内容を別ウィンドウで確認できる GUI ツールもサポートしています: VS Code : code Meld : meld KDiff3 : kdiff3 FileMerge (macOS) : opendiff Vim : vimdiff Neovim : nvim 注意: GUI Diff ツールは表示専用の一時ファイルを開きます。GUI ツールで行った編集は保存されず、Kiro の提案された変更には適用されません。 カスタム引数の使用 引用符で囲むことで、ツールの動作をカスタマイズできます。 # delta でサイドバイサイド表示を有効化 kiro-cli settings chat.diffTool "delta --side-by-side" 詳細については、 カスタム Diff ツールのドキュメント を参照してください。 アップデート 3 : AST パターンツールによる正確なリファクタリング 新しい pattern-search と pattern-rewrite ツールにより、エージェントはテキストの正規表現ではなく、構文木パターンを使用してコードを検索および変換できます。これにより、文字列リテラルやコメント内の誤検出がなくなります。 pattern-search の使用例 # すべての async 関数を検索 > async function $NAME($$$PARAMS) { $$$ } という構造のコードを検索して # 特定のメソッド呼び出しを検索 > $OBJ.setState($$$ARGS) のパターンを検索して pattern-rewrite の使用例 # var を const に変換 > var 宣言をすべて const に書き換えて # 古い API を新しい API に変換 > $O.hasOwnProperty($P) を Object.hasOwn($O, $P) に書き換えて メタ変数を使用してパターンを定義します。 $VAR :単一のノード(識別子、式)にマッチ $$$ :ゼロ個以上のノード(文、パラメータ)にマッチ これらのツールは、コードの構造を理解するため、テキストベースの検索置換よりも正確で安全なリファクタリングが可能です。 アップデート 4 : 改善されたコードインテリジェンス Kiro CLI は、セットアップ不要で 18 言語に対応した組み込みのコードインテリジェンスを提供します。エージェントは、シンボル検索、定義へのナビゲーション、構造的なコード検索を即座に実行できます。 対応言語 Bash、C、C++、C#、Elixir、Go、Java、JavaScript、Kotlin、Lua、PHP、Python、Ruby、Rust、Scala、Swift、TSX、TypeScript 組み込み機能 シンボル検索 :コードベース全体で関数、クラス、変数を検索 ドキュメントシンボル :ファイル内のすべてのシンボルをリスト表示 シンボルルックアップ :定義に即座にジャンプ パターン検索 :AST ベースの構造的コード検索 パターン書き換え :AST パターンを使用した自動コード変換 コードベースマップ :ディレクトリ構造の探索とコード構成の理解 コードベース概要 任意のワークスペースの概要を素早く取得できます。 /code overview クリーンな出力には --silent を使用します。 /code overview --silent これは以下の場合に便利です: 新しいコードベースへのオンボーディング プロジェクト構造に関する Q&A セッション 未知のパッケージを素早く理解 LSP 統合(オプション) 参照の検索、ホバードキュメント、リファクタリングのリネームなどの拡張機能を使用するには、LSP 統合を有効にできます。プロジェクトルートで以下のコマンドを実行することで、 .kiro/settings/lsp.json 設定が作成され、言語サーバーが起動します。 /code init 使用例 # シンボルを検索 > UserRepository クラスを検索して # すべての参照を検索 > Person クラスの参照をすべて検索して # 定義に移動 > UserService の定義を検索して # ファイル内のシンボルを取得 > auth.service.ts にはどんなシンボルがある? # ホバードキュメントを取得 > AuthService の authenticate メソッドのドキュメントは? # 利用可能なメソッドを発見 > s3Client インスタンスで使えるメソッドは? 詳細については、 コードインテリジェンスのドキュメント を参照してください。 アップデート 5 : 会話圧縮の詳細なコントロール /compact コマンドを利用することで会話履歴を要約し、重要な情報を保持しながらコンテキストスペースを解放することができます。今回のアップデートでは保持するメッセージと最小コンテキストウィンドウの割合を指定することが可能になりました。 圧縮の仕組み 圧縮は、古いメッセージを要約しながら最近のメッセージを保持します。これにより、会話の文脈を維持しながら、コンテキストウィンドウを効率的に使用できます。 手動圧縮 : /compact コマンドを実行 自動圧縮 :コンテキストウィンドウがオーバーフローすると自動的にトリガー 設定 保持するメッセージの量を設定できます。 compaction.excludeMessages (デフォルト:2):保持する最小メッセージペア数 compaction.excludeContextWindowPercent (デフォルト:2):保持する最小コンテキストウィンドウの割合 両方の設定が評価され、より保守的な(大きい)値が優先されます。 圧縮後の操作 # 手動で圧縮を実行 /compact # 元のセッションに戻る /chat resume 詳細については、 会話の圧縮のドキュメント を参照してください。 アップデート 6 : web_fetch ツールの詳細な URL 権限管理 エージェント設定を通じて、エージェントがアクセスできる URL を制御できるようになりました。正規表現パターンを使用して、信頼できるドメインを自動的に許可したり、特定のサイトをブロックしたりできます。 設定方法 エージェント設定ファイルの toolsSettings で URL ベースの権限を設定します。 { "toolsSettings": { "web_fetch": { "trusted": [".*docs\\.aws\\.amazon\\.com.*", ".*github\\.com.*"], "blocked": [".*pastebin\\.com.*"] } } } パターンの動作 パターンは正規表現で、自動的に ^ と $ でアンカーされます blocked は trusted よりも優先されます blocked の無効な正規表現は、すべての URL を拒否します(フェイルセーフ) trusted の無効な正規表現はスキップされます 信頼されたパターンに一致しない URL は、承認を求めるプロンプトが表示されます 使用例 { "toolsSettings": { "web_fetch": { "trusted": [ ".*docs\\.aws\\.amazon\\.com.*", ".*github\\.com/myorg/.*", ".*stackoverflow\\.com.*" ], "blocked": [ ".*pastebin\\.com.*", ".*privatesite\\.internal.*" ] } } } この設定により、AWS ドキュメント、組織の GitHub リポジトリ、Stack Overflow への自動アクセスが許可され、特定のサイトがブロックされます。 詳細については、 web_fetch ツールのドキュメント を参照してください。 アップデート 7 : リモート認証 リモートマシン(SSH、SSM、コンテナ経由)で Kiro CLI を実行する際、Google または GitHub でサインインできるようになりました。ポートフォワーディングにより、認証が機能します。 Builder ID と IAM Identity Center Builder ID と IAM Identity Center の場合、デバイスコード認証がそのまま機能します。URL とコードをローカルブラウザに入力するだけです。 ソーシャルログイン(Google または GitHub) ソーシャルログインの場合、CLI は PKCE 認証を使用し、ポートフォワーディングが必要です。OAuth コールバックは localhost にリダイレクトされるため、トンネルなしではリモート CLI に到達できません。 リモートマシンでのサインイン手順 kiro-cli login を実行し、「Use for Free with Google or GitHub」を選択 表示されたポート番号をメモ(毎回異なります。例: 49153 ) ローカルマシンの新しいターミナルで、ポートフォワーディングを設定: ssh -L <PORT>:localhost:<PORT> -N user@remote-host <PORT> をステップ 2 のポートに、 user@remote-host をリモート認証情報に置き換えます。 CLI で Enter キーを押し、ローカルブラウザで URL を開きます 認証を完了すると、コールバックがトンネル経由で CLI に到達します SSH ポートフォワーディングの例 # 基本的なポートフォワーディング(49153 を実際のポートに置き換え) ssh -L 49153:localhost:49153 -N user@remote-host # カスタム ID ファイルを使用(EC2 で一般的) ssh -i ~/.ssh/my-key.pem -L 49153:localhost:49153 -N user@remote-host # SSH 設定エイリアスを使用 ssh -L 49153:localhost:49153 -N myserver 詳細については、 リモート認証のドキュメント を参照してください。 移行ガイド 既存の Kiro CLI ユーザーが 1.24.0 にアップグレードする際のガイドラインです。 ステップ 1:常に読み込みが必要ではない Steering ファイルを Skills に変換 既存の Steering ファイルの中に常に読み込みが必要ではないものがある場合は、Skills に変換することを検討してください。 変換前: { "resources": [ "file://docs/api-reference.md", "file://docs/architecture-guide.md" ] } 変換後: 1. 各ファイルに YAML フロントマターを追加 --- name: api-reference description: API リファレンスドキュメント。API エンドポイント、リクエスト/レスポンス形式、認証方法について記載。 --- # API リファレンス ... 2. エージェント設定を更新: { "resources": [ "skill://docs/api-reference.md", "skill://docs/architecture-guide.md" ] } ステップ 2:カスタム Diff ツールの設定 お気に入りの Diff ツールがある場合は、設定してください。 # delta を使用する場合 kiro-cli settings chat.diffTool delta # サイドバイサイド表示を有効化 kiro-cli settings chat.diffTool "delta --side-by-side" ステップ 3:URL 権限の設定 web_fetch ツールを使用している場合は、信頼できるドメインを設定してください。 { "toolsSettings": { "web_fetch": { "trusted": [ ".*docs\\.aws\\.amazon\\.com.*", ".*github\\.com/your-org/.*" ] } } } ステップ 4:コードインテリジェンスの有効化 プロジェクトルートで LSP を初期化 /code init まとめ Kiro CLI 1.24.0 は、開発者の生産性を向上させる多くの新機能を提供します。Skills による効率的なコンテキスト管理、カスタム Diff ツールによる柔軟な変更レビュー、18 言語に対応した組み込みコードインテリジェンス、会話の圧縮による長時間セッションのサポート、詳細な URL 権限管理、リモート認証のサポートなど、開発ワークフローを強化する機能が満載です。 今すぐ Kiro CLI 1.24.0 にアップグレードもしくは インストール して、これらの新機能をお試しください!みなさんの Kiro ライフがより快適になることを願っています! 著者 稲田 大陸 – いなりく AWS Japan で働く Kiro をこよなく愛すソリューションアーキテクト。普段は製造業のお客様を支援しています。その活動の傍ら、最近は AI 駆動開発ライフサイクル (AI-DLC) の日本のお客様への布教活動もしつつ、 Kiro のブログ などを執筆しています。
新年あけましておめでとうございます。ソリューションアーキテクトの konippi です。 Kiro CLI は、2025 年 11 月から 12 月にかけて、v1.21.0・v1.22.0・v1.23.0 と立て続けにアップデートがリリースされました。Web 検索機能、コードインテリジェンス、サブエージェント、Plan エージェントなど、開発体験を大きく向上させる機能が追加されています。Kiro についてまだご存知でない方は「 Kiroweeeeeeek in Japan 開催のお知らせ 」を読んでいただけますと幸いです。また、Kiro のアップデートは Changelog よりご確認ください。 本記事では、これら 3 つのバージョンで追加された主要機能をまとめて紹介します。 v1.21.0 Web 検索・取得機能 Kiro CLI がインターネットからリアルタイムで情報を取得できるようになりました。最新のライブラリバージョン、ドキュメント、技術的な問題の解決策などを、ブラウザに切り替えることなく開発ワークフロー内で直接検索できます。 主な用途 最新のライブラリドキュメントの参照 技術的な問題の解決策の検索 API の最新仕様の確認 使い方 # エージェントが自動的に Web 検索を実行 > React 19 の新機能について調べて教えてください Web 検索機能により、開発中に必要な情報をその場で取得でき、開発フローが中断されることがなくなります。 v1.22.0 コードインテリジェンス機能 v1.22.0 で追加された機能の 1 つは、Language Server Protocol (LSP) 統合による強力なコードインテリジェンス機能です。Kiro IDE と同等のコード理解能力がターミナルで利用できるようになりました。 組み込み機能(セットアップ不要) インストール直後から以下の機能が利用可能です: 対応言語: Bash, C, C++, C#, Elixir, Go, Java, JavaScript, Kotlin, Lua, PHP, Python, Ruby, Rust, Scala, Swift, TSX, TypeScript 主な機能: シンボル検索 : コードベース全体から関数、クラス、変数を検索 定義へのジャンプ : シンボルの定義箇所へ即座に移動 パターン検索 : AST ベースの構造的コード検索 パターン書き換え : AST パターンを使った自動コード変換 コードベース概要 : プロジェクト構造の即座の把握 # コードベース全体の概要を取得 > /code overview # より簡潔な出力 > /code overview --silent LSP 統合(オプション) さらに高度な機能が必要な場合は、LSP サーバーを統合できます: # プロジェクトルートで初期化 > /code init これにより、参照検索、リネームリファクタリング、診断情報、コード補完などの機能が追加されます。 自然言語でのクエリ例: > UserRepository クラスを見つけて > s3Client で使えるメソッドは? LSP サーバーは自動的に検出・起動され、ワークスペースごとに .kiro/settings/lsp.json で設定が管理されます。 対応言語と Language Server 言語 拡張子 Language Server インストールコマンド TypeScript/JavaScript .ts, .js, .tsx, .jsx typescript-language-server npm install -g typescript-language-server typescript Rust .rs rust-analyzer rustup component add rust-analyzer Python .py pyright npm install -g pyright または pip install pyright Go .go gopls go install golang.org/x/tools/gopls@latest Java .java jdtls brew install jdtls (macOS) Ruby .rb solargraph gem install solargraph C/C++ .c, .cpp, .h, .hpp clangd brew install llvm (macOS) または apt install clangd (Linux) カスタム Language Server の追加 上記以外の言語を使用する場合、 .kiro/settings/lsp.json を編集してカスタム Language Server を追加できます: { "languages": { "mylang": { "name": "my-language-server", "command": "my-lsp-binary", "args": ["--stdio"], "file_extensions": ["mylang", "ml"], "project_patterns": ["mylang.config"], "exclude_patterns": ["/build/"], "multi_workspace": false, "initialization_options": { "custom": "options" }, "request_timeout_secs": 60 } } } 編集後、Kiro CLI を再起動して設定を反映してください。 ※ コードインテリジェンス機能はワークスペース単位で設定されます。各プロジェクトで独立して管理されるため、プロジェクトごとに /code init を実行する必要があります。 Knowledge Management 機能 Knowledge Management 機能により、プロジェクト固有のドキュメントやコードベースを永続的な知識ベースとして管理できるようになりました。この機能は Experimental 機能であり、使用前に有効化が必要です。 2つのインデックスタイプ: Fast (Lexical – BM25) : 高速なキーワードベース検索。ログファイルや設定ファイルに最適 Best (Semantic – all-minilm-l6-v2) : 意味を理解するセマンティック検索。ドキュメントや研究論文に最適 基本的な使い方 # 機能を有効化 $ kiro-cli settings chat.enableKnowledge true # ディレクトリをインデックスに追加 $ /knowledge add --name "project-docs" --path /path/to/documentation # セマンティック検索を使用 $ /knowledge add --name "api-docs" --path /path/to/docs --index-type Best # パターンフィルタリング > /knowledge add --name "rust-code" --path /path/to/project \ --include "*.rs" --exclude "target/*" # ナレッジベースを確認 > /knowledge show エージェント別の独立した知識ベース 各エージェントは独自の知識ベースを持ち、コンテキストの混在を防ぎます: # デフォルトエージェントで知識を追加 > /knowledge add /path/to/docs # カスタムエージェントに切り替え > kiro-cli chat --agent my-custom-agent # カスタムエージェント専用の知識ベースを作成 > /knowledge add /path/to/agent/docs 知識ベースはセッション間で永続化され、自然言語クエリで検索できます。 v1.23.0 サブエージェント機能 複雑なタスクを専門的なサブエージェントに委譲できる機能が追加されました。サブエージェントは独自のコンテキストを持ち、並列実行が可能です。 主な特徴 自律実行 : 独自のコンテキストで独立して実行 リアルタイム進捗追跡 : タスク実行中の状態をリアルタイムで確認 並列タスク実行 : 複数のサブエージェントを同時実行 カスタムエージェント設定のサポート : 専門化されたエージェントを利用可能 コアツールへのアクセス : ファイル読み書き、シェルコマンド、MCP ツールの使用 結果の自動集約 : 完了時に結果をメインエージェントに自動返却 使用例 # デフォルトのサブエージェントを使用 > 複数のデータソースを並列で調査して # カスタムエージェントをサブエージェントとして使用 > 商品アイテムの一覧を返す API エンドポイントを backend エージェントで、 商品アイテム一覧ページの UI コンポーネントを frontend エージェントで実装して カスタムエージェントを使用することで、フロントエンドとバックエンドの同時開発など、より高度なワークフローを実現できます。 ※ 既存のエージェント設定でツールを制限している場合は、 subagent ツールを許可リストに追加する必要があります。 Plan エージェント機能 アイデアを構造化された実装計画に変換する専用のビルトインエージェントが追加されました。要件収集、リサーチを通じて、実装前に詳細なタスク分解を作成します。 アクセス方法 キーボードショートカット: Shift + Tab キー(Plan モードと実行モードを切り替え) スラッシュコマンド: /plan プロンプトと同時に起動: /plan 商品一覧を取得する API エンドポイントを実装したい ワークフロー 要件収集 : 構造化された質問でアイデアを洗練 リサーチと分析 : コードインテリジェンス、grep、glob ツールを使用してコードベースを探索 実装計画の作成 : 明確な目標とデモ説明を含む詳細なタスク分解を作成 計画の承認と引き継ぎ : 承認された計画を実行エージェントに転送 各タスクに含まれる情報: Problem Statement (問題定義) : 何を作るのか、解決すべき課題 Requirements (要件) : 実装すべき機能と制約 Background (背景) : 技術的な意思決定の理由と前提知識 Proposed Solution (提案する解決策) : 実装アプローチの全体的な戦略 Task Breakdown (タスク分解) : 目標、実装ガイダンス、Demo Plan エージェントは読み取り専用モードで動作し、ファイルを変更せずに計画に集中します。コードベース探索、Language Server、Grep / Glob ツール、Web 検索は利用可能ですが、ファイル書き込みやコマンド実行、MCP ツールは使用できません。 Grep / Glob ツール 高速なファイル検索のための2つの新しいビルトインツールが追加されました。 Grep ツール : 正規表現を使用した高速コンテンツ検索。bash の grep 、 rg 、 ag コマンドの代替として使用 Glob ツール : Glob パターンを使用した高速ファイル発見。bash の find コマンドの代替として使用 両ツールは .gitignore を自動的に尊重し、カレントディレクトリでデフォルトで信頼されます。 使用例 # エージェントが自動的に使用 > すべての Rust ファイルで 'async fn' を検索して > src ディレクトリ内の全ての .ts ファイルを見つけて shell ツールとの違い: 項目 Grep / Glob ツール shell ツール .gitignore 自動的に尊重 考慮しない 速度 高速 標準 カレントディレクトリ デフォルトで信頼 デフォルトで確認が必要 エージェント設定で allowedPaths と deniedPaths を設定してアクセス範囲を制御できます。 マルチセッションサポート 複数のチャットセッションを管理できるインタラクティブなセッションピッカーが追加されました。セッションは起動したディレクトリごとに保存され、会話のターンごとに自動保存されます。 コマンドラインからの操作 # セッションピッカーを開く $ kiro-cli chat --resume-picker # 最新のセッションを再開 $ kiro-cli chat --resume # または -r # セッション一覧を表示 $ kiro-cli chat --list-sessions # または -l # セッションを削除 $ kiro-cli chat --delete-session <SESSION_ID> # または -d チャットセッション内からの操作 # セッションピッカーを開く > /chat resume # セッションをファイルに保存 > /chat save <FILE_PATH> # ファイルからセッションを読み込む(.json 拡張子は省略可能) > /chat load <FILE_PATH> セッションピッカーには、セッション名、最終アクティビティ、メッセージプレビューが表示されます。 MCP Registry サポート MCP レジストリサポートにより、組織レベルでの MCP ツールのガバナンス機能が追加されました。管理者は利用可能な MCP ツールを管理・制御でき、チーム全体での一貫性とセキュリティを確保できます。 まとめ Kiro CLI v1.21.0 から v1.23.0 にかけて、以下の主要機能が追加されました: v1.21.0 : Web 検索・取得機能によるリアルタイム情報アクセス v1.22.0 : コードインテリジェンス機能 (LSP 統合によるさらに高度なコード操作) Knowledge Management 機能による永続的な知識ベース管理 (Experimental 機能) v1.23.0 : サブエージェントによる並列タスク実行 Plan エージェントによる構造化された実装計画 Grep / Glob ツールによる高速ファイル検索 マルチセッションサポート MCP Registry によるガバナンス機能 これらのアップデートにより、Kiro CLI はターミナル上での AI 駆動開発体験をさらに強化し、より複雑なタスクを効率的に処理できるようになりました。 まだ Kiro CLI を試していない方は、 公式サイト からダウンロードできるのでぜひ試してみてください。また、 Kiro のドキュメント を参照し、ぜひたくさんの機能を触ってみてください! 著者 小西 杏典 (Kyosuke Konishi) アマゾンウェブサービスジャパン合同会社 ソリューションアーキテクト 2025 年に新卒入社した 1 年目のソリューションアーキテクト konippi です。Amazon Q Developer CLI をはじめ、いろいろな OSS に Contribution することが好きです。 X : https://x.com/_konippi LinkedIn : https://www.linkedin.com/in/kyosuke-konishi GitHub : https://github.com/konippi
一休.com Advent Calendar 2025 の25日目の記事です。 一休.com レストランの開発を担当している恩田 @takashi_onda です。 最近はあまり聞かれることのないダイナミックスコープの話をしてみたいと思います。 はじめに 現代のプログラミング言語ではレキシカルスコープがあまりに当たり前になってしまっていて、ダイナミックスコープという概念自体を聞いたことがない、という人も多いのではないかと思います。 プログラミング言語の歴史を学ぶ際に少し触れられている程度で、実際、手元の『コンピュータプログラミングの概念・技法・モデル』を繙いてみても、900ページ近い大著にもかかわらずダイナミックスコープについての言及は1ページにも満たないほどです。 このようにダイナミックスコープは歴史の中で消えていった概念のように見えます。ですが、用語としては廃れた一方で、今日でも似た仕組み自体が実は再発明されています。使い方に注意は必要ですが、うまくはまると既存コードへの侵襲を最小に抑えながら文脈を伝播させる手段として、今も有効な選択肢だからではないでしょうか。 本稿では、ダイナミックスコープの歴史を振り返りながら、なぜ今も形を変えてその考え方が引き継がれているのか、文脈伝播の観点から見直してみたいと思います。 レキシカルスコープとダイナミックスコープ まずは定義の確認からはじめたいと思います。 現代のプログラミング言語において、私たちが当たり前のように享受しているのがレキシカルスコープ(静的スコープ)です。 const x = 'Global' ; function printX ( suffix ) { const prefix = 'value is ' console . log ( ` ${ prefix }${ x }${ suffix } ` ) ; } function withLocalX () { const x = 'Local' ; printX ( '!' ) ; } withLocalX () ; // -> 'value is Global!' printX ( '?' ) // -> 'value is Global?' ここで、 printX に現れる変数に注目して、用語 1 をふたつ紹介します。 束縛変数(bound variable): 関数の引数( suffix )や内部での宣言( prefix )によって、その場で意味が確定する変数を指します。 自由変数(free variable): 関数の中で宣言も引数定義もされていない変数を指します。この例では x がそれにあたります。 レキシカルスコープとダイナミックスコープの違いは、この自由変数をどう解決するかにあります。 レキシカルスコープのルールはシンプルです。自由変数の意味は関数が定義された場所によって静的に決まる、というものです。上の例では printX が定義された場所の外側にある値 'Global' が参照されます。呼び出し元である withLocalX の内部に同名の変数があっても、それは無視されます。 この性質により、私たちはコードの構造から変数の由来を一意に辿ることができるという恩恵に与っています。 ごくごく自然に感じられると思います。 さて、今回取り上げるダイナミックスコープ(動的スコープ)を見てみましょう。ダイナミックスコープは、自由変数の解決をコード上の位置ではなく、実行時の呼び出しスタックに委ねます。 Perl の local 宣言 2 を例に見てみましょう。 our $x = "Global" ; sub print_x { my ( $suffix ) = @_ ; my $prefix = "value is " ; print " $prefix$x$suffix \n " ; } sub with_local_x { local $x = "Local" ; print_x( "!" ); } with_local_x(); # -> "value is Local!" print_x( "?" ); # -> "value is Global?" print_x が呼ばれる際、その自由変数 $x の値は自分を呼び出している実行時のコールスタックの状態で決定されます。 with_local_x の中で $x が一時的に変更されているため print_x はその値 "Local" を出力します。そして with_local_x の実行が終われば、その一時的な束縛が解除され $x の値はふたたび "Global" が参照されるようになります。 ダイナミックスコープの歴史 現代の感覚では、ダイナミックスコープは予測不能で不確実なものに見えると思います。では、なぜこのような仕組みが生まれ、利用されてきたのでしょうか。その経緯を振り返ってみたいと思います。 副産物としての誕生 ダイナミックスコープの起源は、1950年代後半の初期の Lisp に遡ります。 初期の Lisp においてダイナミックスコープは、意図的に設計された機能というよりは、素朴な実装の帰結でした。当時のインタプリタにおいて変数の値を解決するもっとも単純な方法は、実行時のシンボルテーブル(A-list と呼ばれる連想リスト)をスタックの根元に向かって順に検索することでした。関数を定義時の環境と一緒に保持するという発想(後にクロージャと呼ばれるもの)はまだなく、この素直な実装が、結果としてダイナミックスコープを生み出しました。 John McCarthy は後に、ダイナミックスコープを、意図した仕様ではなく単なる実装上のバグであり、いずれ修正されるだろうと考えていたと回想しています 3 。 引数バケツリレーの回避策としての受容 しかし、この偶然の挙動は実用上の利便性をもたらしました。 プログラムが複雑化し、関数の呼び出し階層が深くなると、末端の処理で必要になる設定値やフラグを、すべての中間関数に引数として渡し続ける必要が出てきます。いわゆるバケツリレー問題ですね。 ダイナミックスコープを利用すれば、呼び出し元で変数を一時的に束縛するだけで、中間層のコードを一切変更することなく、深い階層にある関数に情報を伝播させることができました。 Scheme によるレキシカルスコープの確立 この状況に変化をもたらしたのが、1970年代に登場した Scheme です。 Gerald Jay Sussman と Guy L. Steele Jr. は、ラムダ計算の理論を忠実に実装する過程で、関数が定義された時点の環境を保持するレキシカルスコープを導入しました。これにより、関数の挙動が呼び出し元に依存するという不確実性が排除され、数学的な一貫性とモジュールとしての独立性が確保されました。 これ以降、プログラミング言語のメインストリームはレキシカルスコープへと収束していき、ダイナミックスコープは扱いの難しいかつての仕組みとして、多くの言語から姿を消していくことになります。 Emacs Lisp における意図的な選択 Scheme がレキシカルスコープによって数学的に整合したモデルを確立していった一方で、Emacs Lisp は長らくダイナミックスコープをデフォルトとして採用し続けました 4 。 当時の計算資源の制約といった実装上の理由もあったようですが、結果としてこの選択は、実行時に振る舞いを拡張・上書き可能なエディタ、というより環境であった Emacs の目指すところと噛み合っていたように思います。 エディタの拡張においては、既存のコマンドやその内部実装に手を入れることなく、ある処理の文脈だけを少し変更したい、という要求が頻繁に現れます。Emacs Lisp では、こうした要求をダイナミックスコープによって自然に満たすことができました。 よく知られている例が、検索時の大文字・小文字の区別を制御する case-fold-search という変数です。この変数を let によって一時的に束縛するだけで、その内部で呼ばれる標準の検索コマンド群の挙動をまとめて変更できます。 ( defun my-case-sensitive-search ( keyword ) ( let (( case-fold-search nil )) ( search-forward keyword ))) 文脈伝播(Context Propagation) プログラミング言語全体に立ち返れば、前述の通り主流となったのはレキシカルスコープでした。関数の振る舞いが呼び出し元の状態に依存する性質は、大規模化・複雑化するソフトウェア開発において、扱いが難しかったためです。 レキシカルスコープがコードの予測可能性をもたらした一方で、アプリケーション開発には別の課題が残されました。文脈の伝播(Context Propagation)です。 Webアプリケーションを例にとれば、認証情報やトレーシングIDなどの情報は、処理の開始から終了まで、あらゆる階層の関数で参照したくなる横断的な関心事 5 です。レキシカルスコープでナイーブに実装すると、すべての関数にバケツリレーで渡さなければならず、中間層は不要な責務を負うことになります。 この明示的な記述の煩雑さを避けるため、言語仕様の外側でダイナミックスコープ的な挙動を実現する仕組みが実用化されてきました。Java における ThreadLocal がその代表例です。言語レベルでは静的なスコープによる安全性を選びつつも、ランタイムで暗黙的に文脈を引き継ぐ機構が初期から用意されていました。 ここからしばらく、現代のプログラミング言語で文脈伝播がどう実現されているかを見ていきたいと思います。 各節の細部を追わなくても、明示的に渡すアプローチと暗黙的に伝播させるアプローチがそれぞれ存在する、という雰囲気だけ掴んでもらえれば十分です。 Go の context パッケージ まずは明示的に文脈を渡す例として Go を見てみます。Go では context.Context を関数の第一引数として渡す規約が確立されており、キャンセル処理やタイムアウト、リクエストスコープの値を伝播させます。 func HandleRequest(w http.ResponseWriter, r *http.Request) { ctx := r.Context() traceId := generateTraceID() ctx = context.WithValue(ctx, traceIdKey, traceId) result, err := processOrder(ctx, orderId) // ... } func processOrder(ctx context.Context, orderId string ) (*Order, error ) { // 中間層も ctx を受け取り、下位に渡す return repository.FindOrder(ctx, orderId) } func (r *Repository) FindOrder(ctx context.Context, orderId string ) (*Order, error ) { traceId := ctx.Value(traceIdKey).( string ) r.logger.Info( "finding order" , "traceId" , traceId, "orderId" , orderId) // ... } 文脈が引数として明示されるため、関数シグネチャを見ればその関数が文脈を必要とすることが分かります。 しかし、 context.WithValue で渡される値については事情が異なります。 ctx に何が入っているかはシグネチャからは分からず、実行時に ctx.Value(key) で取り出すまで不明です。つまり、 context.Context という引数は明示的に渡されていますが、その中身へのアクセスはキーによる動的な参照になっています。 では、型によってこの暗黙性を解消する方法はあるのでしょうか。 Reader Monad 関数型プログラミングの世界では、この課題に対する手法として Reader Monad が知られています。 Reader Monad の本質は単純です。環境 R を受け取って値 A を返す関数 R => A を、合成可能な形で扱えるようにしたものです。Scala で書いてみましょう。 case class Reader[R, A](run: R => A) { def map[B](f: A => B): Reader[R, B] = Reader(r => f(run(r))) def flatMap[B](f: A => Reader[R, B]): Reader[R, B] = Reader(r => f(run(r)).run(r)) } これで環境に依存する計算を合成可能な形で表現できます。さきほどの例を Reader Monad で実装します。 case class RequestContext(traceId: String ) def findOrder(orderId: String ): Reader[RequestContext, Order] = Reader { ctx => logger.info(s "finding order: traceId=${ctx.traceId}, orderId=$orderId" ) repository.find(orderId) } def processOrder(orderId: String ): Reader[RequestContext, Result] = for { order <- findOrder(orderId) result <- validateAndProcess(order) } yield result def handleRequest(orderId: String ): Reader[RequestContext, Response] = for { result <- processOrder(orderId) } yield Response(result) // 実行時に環境を注入 val ctx = RequestContext(traceId = "abc-123" ) val response = handleRequest( "order-789" ).run(ctx) 関数のシグネチャに注目してください。 Reader[RequestContext, Order] という戻り値の型を見るだけで、この関数が RequestContext を必要とすることが分かります。必要な文脈が型レベルで明示されています。 また、for 内包表記により、環境の受け渡しを省略できます。 processOrder は findOrder を呼び出していますが、 ctx を渡すコードはどこにもありません。Reader の flatMap が環境を伝播してくれるからです。 この手法により、文脈の明示性と記述の簡潔さを両立できます。 Scala の context parameter このような書き方はよく使われるため、Scala では性質の近い機能が言語レベルでサポートされています。 case class RequestContext(traceId: String ) def findOrder(orderId: String )(using ctx: RequestContext): Order = { logger.info(s "finding order: traceId=${ctx.traceId}, orderId=$orderId" ) repository.find(orderId) } def processOrder(orderId: String )(using ctx: RequestContext): Result = { val order = findOrder(orderId) // ctx は暗黙的に渡される validateAndProcess(order) } def handleRequest(orderId: String )(using ctx: RequestContext): Response = { val result = processOrder(orderId) // ctx は暗黙的に渡される Response(result) } // 呼び出し側で given を定義 given ctx: RequestContext = RequestContext(traceId = "abc-123" ) val response = handleRequest( "order-789" ) // ctx は暗黙的に解決される using キーワードにより、コンパイラがスコープ内から適切な値を探して自動的に引数を補完します。中間層での明示的な受け渡しが不要でありながら、シグネチャには文脈が明示されています。 これは、レキシカルスコープの型安全性を維持しつつ、ダイナミックスコープが解決していたバケツリレー問題に対処する言語レベルの解答と言えます。 ただし、中間層の関数も (using ctx: RequestContext) をシグネチャに持つ必要があり、文脈の存在自体は伝播経路上のすべての関数に現れます。 ThreadLocal / AsyncLocalStorage ここまで見てきたのは、いずれも文脈を明示的に表現する手法でした。次に、暗黙的に文脈を伝播させる仕組みを見ていきます。 Java の ThreadLocal は JDK 1.2(1998年)で導入されました。ThreadLocal は、スレッドごとに独立した値を保持する仕組みです。 Webアプリケーションでは、1つのリクエストが1つのスレッドで処理される実行モデルが一般的でした。このモデルにおいて、リクエストスコープの情報(認証情報やトランザクションなど)を、引数で渡すことなく処理の流れ全体で共有する用途で ThreadLocal は広く使われてきました。 先ほどと同じ例を Java で書いてみましょう。 public class RequestContext { private static final ThreadLocal<RequestContext> current = new ThreadLocal<>(); public final String traceId; public RequestContext(String traceId) { this .traceId = traceId; } public static RequestContext current() { return current.get(); } public static <T> T runWith(RequestContext ctx, Supplier<T> block) { RequestContext previous = current.get(); current.set(ctx); try { return block.get(); } finally { current.set(previous); } } } public Order findOrder(String orderId) { var ctx = RequestContext.current(); logger.info( "finding order: traceId=" + ctx.traceId + ", orderId=" + orderId); return repository.find(orderId); } public Result processOrder(String orderId) { var order = findOrder(orderId); return validateAndProcess(order); } public Response handleRequest(String orderId) { var result = processOrder(orderId); return new Response(result); } // エントリーポイント var ctx = new RequestContext( "abc-123" ); var response = RequestContext.runWith(ctx, () -> handleRequest( "order-789" )); findOrder も processOrder も引数に文脈を持っていません。 RequestContext.current() を呼び出すだけで、呼び出し元で設定された値を取得できます。そして runWith のブロックを抜ければ、以前の値に戻ります。Perl の local が実現していた振る舞いと同じですね。 現在では非同期・並行処理が一般的になり、それらに対応した Java の ScopedValue(JDK 21〜、プレビュー)や、Node.js の AsyncLocalStorage が同様の機能を提供しています。これらは値のネストと復元が API に組み込まれており、ダイナミックスコープがコールスタックを遡って値を解決する仕組みにより近いものになっています。 React Context ここで少し視点を変えて、フロントエンドに目を向けてみましょう。 関数呼び出しの連鎖がコールスタックを形成するように、React ではコンポーネントの親子関係がツリー構造を形成します。そしてここでも、同じバケツリレー問題が現れます。 React では、親から子へデータを渡す際に props を使います。しかし、深くネストしたコンポーネントに値を届けるには、途中のすべてのコンポーネントが props を受け取って下に渡す必要があります。いわゆる props drilling です。 中間層のコンポーネントが自身では使わない props に依存することは、コンポーネントの再利用性を損ない、不要な再レンダリングの原因にもなります。React Context を使えば、Context で囲んだ範囲内のどの深さのコンポーネントからでも、中間層を経由せずに値を取得できます。 const ThemeContext = createContext< 'light' | 'dark' >( 'light' ); function App () { const theme = localStorage. getItem ( 'theme' ) ?? 'light' ; return ( < ThemeContext value = { theme } > < Header /> < Main /> < Footer /> </ ThemeContext > ); } function Main () { // Main は theme を知らない return < Sidebar /> ; } function Sidebar () { const theme = use(ThemeContext); // 中間層を飛び越えて取得 return < div className = { theme } > ... </ div > ; } Context のネストによって値を上書きでき、そのスコープを抜ければ外側の値に戻る。コンポーネントツリーという軸は異なりますが、これもダイナミックスコープの再発見と言えそうです。 侵襲を抑える ここまで見てきた通り、文脈伝播には万能の解決策がありません。 明示的に引数で渡せば、依存関係は明確になりコードの追跡も容易です。しかし、中間層が自身では使わない引数を知らなければならないという問題が残ります。暗黙的な伝播を使えば中間層の負担は消えますが、今度は依存関係が見えにくくなります。 このトレードオフに対して、別の軸から考えてみたいと思います。既存コードへの侵襲を抑える、という制約を置いた場合、ダイナミックスコープ的な振る舞いはどのように評価できるでしょうか。 現実のコードベースは往々にして理想通りにはなっていません。テストや文脈伝播の仕組みは必要だとわかっていても、スケジュールや優先度の都合で後回しにしたまま、コードが蓄積されてしまうことは起こりがちです。そこに手を入れるとき、引数で明示的に渡したり Reader Monad を導入するのが正攻法ですが、中間層をすべて修正するコストが見合わないこともあります。 以下では、ダイナミックスコープ的な仕組みの利用が、妥協ではあっても有効な選択肢になった例を具体的に見ていきます。いずれも呼び出し元の文脈に応じて値を差し替えたいという要求であり、これはまさにダイナミックスコープが解決していた問題です。実際に遭遇したケースを簡素化して紹介します。 あとからテストダブル たとえば、外部 API を直接呼び出している関数があり、テストを書きたいとします。理想的には依存性注入で差し替えられる設計になっているべきですが、現実にはそうなっていないコードも多いでしょう。 このとき、API クライアントを AsyncLocalStorage 経由で参照するように変更すれば、テスト時だけテストダブルを差し込むことができます。中間層の関数シグネチャを変更する必要はありません。 具体例を見てみましょう。 // 本番用の取得関数をデフォルト値として設定 const fetchCategoriesContext = new AsyncLocalStorage< ( ids : CategoryId []) => Promise < Category []> >( { defaultValue : defaultFetchCategories } ) function getFetchCategories () { const fetchCategories = fetchCategoriesContext.getStore() if (!fetchCategories) { throw new Error ( 'unreachable: defaultValue is set' ) } return fetchCategories } この getFetchCategories を利用してカテゴリを取得する関数を定義します。 export async function getCategory ( id : CategoryId ): Promise < Category | undefined > { const fetchCategories = getFetchCategories() const categories = await fetchCategories( [ id ] ) return categories[ 0 ] } テスト時にはテストダブルを差し込む関数を用意します。 /** * テスト時に fetchCategories を差し替えて実行する */ export async function withTestFetchCategories < T >( fetchCategories : ( ids : CategoryId []) => Promise < Category []> , body : () => T | Promise < T > ): Promise < T > { return fetchCategoriesContext.run(fetchCategories, body) } テストコードでは、 withTestFetchCategories のスコープ内でテスト対象を呼び出します。 getCategory を利用しているコードも、同じスコープ内で実行すればテストダブルが注入されます。 test ( 'カテゴリを取得できる' , async () => { const stubFetch = async ( ids : CategoryId []) => [ { id : ids[ 0 ], name : 'テストカテゴリ' } ] await withTestFetchCategories(stubFetch, async () => { const result = await getCategory( 'cat-1' ) expect (result?. name ).toBe( 'テストカテゴリ' ) } ) } ) あとからキャッシュ 先ほどのカテゴリデータの例の続きです。ひとつのリクエストを処理する中で getCategory が何度も呼ばれていることがわかりました。毎回バックエンドから取得せずに済むようにキャッシュを導入しましょう。 DataLoader を使えばキャッシュできますが、グローバルにキャッシュすると更新の反映やメモリ管理が複雑になります。そこでリクエスト単位でインスタンスを作ることにしました。具体的には、DataLoader をリクエストスコープで保持するために AsyncLocalStorage をもうひとつ追加します。 type CategoryDataLoader = DataLoader < CategoryId , Category | undefined > const categoryDataLoaderContext = new AsyncLocalStorage< CategoryDataLoader >() /** リクエスト単位で DataLoader を保持する */ export async function withCategoryDataLoader < T >( request : Request , body : () => T | Promise < T > ): Promise < T > { const loader = createCategoryDataLoader(request) return categoryDataLoaderContext.run(loader, body) } function createCategoryDataLoader ( request : Request ): CategoryDataLoader { const fetchCategories = getFetchCategories() return new DataLoader< CategoryId , Category | undefined >( async ( ids ) => fetchCategories(ids, request), { cache : true } ) } getCategory は DataLoader 経由で取得するように書き換えます。 function getCategoryDataLoader (): CategoryDataLoader { const loader = categoryDataLoaderContext.getStore() if (!loader) { throw new Error ( 'No categoryDataLoader in context' ) } return loader } // 利用側は DataLoader の存在を意識しない export async function getCategory ( id : CategoryId ): Promise < Category | undefined > { return getCategoryDataLoader().load(id) } リクエストを受けるエントリーポイントで withCategoryDataLoader を適用します。 export async function loader ( { request } : Route.LoaderArgs ) { return await withCategoryDataLoader(request, async () => { // この中で getCategory を呼び出す処理 } ) } これで、中間層の関数が DataLoader を引き回す必要はなく、リクエスト単位のキャッシュが有効になります。 あとから文脈伝播 実は上の例では、キャッシュだけでなく文脈伝播も実現しています。 fetchCategories の実装を見てみましょう。 async function defaultFetchCategories ( ids : readonly CategoryId [] , request : Request ): Promise <( Category | undefined )[]> { const response = await fetch (BACKEND_API, { method : 'POST' , headers : { 'Content-Type' : 'application/json' , 'X-Forwarded-For' : request. headers . get ( 'X-Forwarded-For' ) ?? '' , 'Cookie' : request. headers . get ( 'Cookie' ) ?? '' , } , body : JSON . stringify ( { ids } ), } ) return response.json() } withCategoryDataLoader に渡された request が DataLoader の生成時にキャプチャされ、バックエンドへのリクエスト時に cookie や X-Forwarded-For ヘッダを引き継いでいます。 getCategory を呼び出す側は、この伝播の仕組みを意識する必要がありません。 必要になったときに、コードの変更を最小に保ちながら、段階的に導入できる点がこのアプローチの利点です。 一方で、エントリーポイントで withCategoryDataLoader の適用を忘れると実行時エラーになる、という脆さがあります。依存関係が型に現れないため、コンパイル時には検出できません。これはダイナミックスコープ的な仕組みに共通する課題であり、トレードオフとしての慎重な検討が必要です。 おわりに React Context を説明していたときに、ダイナミックスコープの話をしたことがありました。それがこの記事のきっかけです。 歴史をたどりながら今の技術の位置づけを見直してみるのも、ときにはおもしろいものです。本稿からその一端でも感じていただければ幸いです。 一休では、技術を深く理解しながら、よりよいシステムをともに作っていくエンジニアを募集しています。 www.ikyu.co.jp まずはカジュアル面談からお気軽にご応募ください! job.persona-ats.com ラムダ計算においては、ラムダ抽象 λx. M の本体 M に現れる変数のうち、λx によって束縛されているものを束縛変数、それ以外を自由変数と呼びます。 ↩ 古い Lisp の例を考えていたのですが Perl でも local で書けることを同僚が教えてくれました。 ↩ John McCarthy, History of Lisp (1978). "In modern terminology, lexical scoping was wanted, and dynamic scoping was obtained. I must confess that I regarded this difficulty as just a bug and expressed confidence that Steve Russell would soon fix it." ↩ 現在の Emacs Lisp ではレキシカルスコープを選択することが可能です。 ↩ 横断的関心事(cross-cutting concern)といえば2000年代に注目された AOP(Aspect Oriented Programming, アスペクト指向プログラミング)ですが、その主要なユースケースのひとつに文脈伝播の自動化がありました。ログ出力やトランザクションのコンテキストなどは、ThreadLocal 等の操作を裏側で隠蔽する典型的な例でした。 ↩
動画
該当するコンテンツが見つかりませんでした













