- TOP
- ã¿ã°äžèЧ
- Lisp
Lisp
ã€ãã³ã
該åœããã³ã³ãã³ããèŠã€ãããŸããã§ãã
ãã¬ãžã³
該åœããã³ã³ãã³ããèŠã€ãããŸããã§ãã
æè¡ããã°
é»é ç·ç ã¯ãã¹ ã€ãããŒã·ã§ã³ æ¬éšã®å±±äžã§ãã2025幎11æ-12æã«ãããŠéå¬ãããKiroã® Hackathon ã€ãã³ãã§ããKiroweenã«åå ããŸããã®ã§ããã®ã¬ããŒãããéãããŸãã ãã®ã€ãã³ãã¯Kiroã䜿ã£ãŠã¢ããªã±ãŒã·ã§ã³ãéçºããããšãç®çãšãã ããã«ãœã³ ã€ãã³ãã§ãã äœããã®ã®ããŒãããããŠã£ã³ãã¢ããŒãã«ããã€ãã³ãã«ãªã£ãŠããŸãã åå èŠä»¶ãªã© 以äžã®ãããªåå èŠä»¶ã«ãªã£ãŠããŸããã å®éã®è©³çŽ°ã¯ å
¬åŒãµã€ã ãã芧ãã ããã åºæ¬çã«Kiroã䜿ã£ãŠã¢ããªã±ãŒã·ã§ã³éçºãããã°ããã®ã§ãããããŒããæå®ãããŠããã®ãç¹åŸŽã§ãã Resurrection: ãæ°ã«å
¥ãã®æè¡ã埩掻ããã Frankenstein: è€æ°ã®æè¡ãçµã¿åãããŠã¢ããªãäœã Skeleton Crew: ã¹ ã±ã«ã ã³ãäœæããããããè€æ°ã®ã¢ããªãäœã Costume Contest: æŽç·Žãããäžæ°å³ãªãã¶ã€ã³ã®ã¢ããªãäœã ãšãã£ãããŒãã®ããã§ã(æ¥æ¬èªèš³ã¯çè
ã«ãã)ã èªåã¯Resurrectionãéžã³ãŸãããåå ããã«ãããããŒãéžå®ã«ããªãæ©ãã ã®ã§ãããç¥äººããèªåãæ®æ®µ Common Lisp ã䜿ã£ãŠããŠãããã¯ååã«å€ãæè¡ãªã®ã§ã¯ãšããææãåããŠã確ãã«ãã®éãã ãªãšããããšã§æ±ºããŸããã äœã£ããã® Kabotanãšããã¢ããªã±ãŒã·ã§ã³ãå®è£
ããŸãããKabotan㯠Common Lisp ã䜿ã£ãŠäœã£ããHTMXãšLLMãçµã¿åãããã¢ããªã±ãŒã·ã§ã³ã§ãããããŠã£ã³ã«ã¡ãªãã æ©èœãæäŸããŠããŠã質åã«çãããããããŠã£ã³ã«é¢ããæç« ãçæãããããããšãã§ããŸãã ãªã Common Lisp ãæ¡çšããããšãããšãå€ãæè¡ãšèŠãªãããŠãããããŒãã®Resurrectionã«ãåã£ãŠããããã§ããäžæ¹ã§èªåã¯æ®æ®µãããªãã« Common Lisp ã䜿ã£ãŠããã®ã§å°ãã§ã Common Lisp ã®è¯ããç¥ã£ãŠããããããšæãéžã³ãŸããã Kabotanã¯ä»¥äžã®URLã§å
¬éããŠããŸãã https://github.com/dentsusoken/kabotan/ Kabotanã¯ä»¥äžã®ãã㪠ã¢ãŒããã¯ã㣠ã«ãªã£ãŠããŸãã ããã³ããšã³ã: HTMX + Tailwind CSS ããã¯ãšã³ã: Common Lisp (clack + hunchentoot) LLM: llama.cppãå©çšããããŒã«ã«ã¢ãã«(gpt- oss -120bãªã©ãæ³å®) ã¢ãã³ãªã¢ããªã±ãŒã·ã§ã³ã§ã¯ããã³ããšã³ãã«ReactãVue.jsãªã©ã® JavaScript ãã¬ãŒã ã¯ãŒã¯ ã䜿ãããšãå€ãã§ãããä»åã¯ã·ã¹ãã ã®å€§éšåã Common Lisp ã§å®è£
ãããã£ããããHTMXãæ¡çšããŸããã ããã³ããšã³ãã«HTMXã䜿ãããšã§ããã³ããšã³ãã® JavaScript ã³ãŒããæå°éã«æããã¢ããªã±ãŒã·ã§ã³ã®å€§éšåã Common Lisp ã§å®è£
ããããšãã§ããŸããã å®éã®ç»é¢ã®äŸã以äžã«ç€ºããŸãã ç¹ã«å ã³ã³ããŒãã³ã éã®ããåãã§ã¯Server Sent Event(SSE)ãå©çšããŠãLLMããã®å¿çããªã¢ã«ã¿ã€ã ã«åãåããããã«ããŠããŸããããã«ããããŠãŒã¶ã¯LLMãå¿çãçæããŠããéã鲿ã確èªã§ãããã ã€ã³ã¿ã©ã¯ãã£ã ãªäœéšãå¯èœãšãªã£ãŠããŸãã å人çã«ã¯ Common Lisp ã§ãçŸä»£çãªã¢ããªã±ãŒã·ã§ã³ã®å®è£
ã¯ååã«å¯èœãšããããšã瀺ããã®ã§ã¯ãªãããšæããŸãã ã¡ãªã¿ã«ã Common Lisp ãå«ã Lisp ç³»ã®èšèªã¯æ¬åŒ§ãå€ãããšã§æåã§ããæ
£ãããšSåŒã¯èªã¿ãããã®ã§ãããªããªããšèŠåŽãããããããŸãããäŸãã°Kabotanã®index.htmlãè¿ãéšåã¯ä»¥äžã®ãããªã³ãŒãã«ãªã£ãŠããŸãã ( defun serve-index ( env ) "Serve the main index.html page. The Lack session middleware automatically handles session cookies, so we don't need to manually set them here." ( declare ( ignore env )) ( let (( html ( uiop:read-file-string "public/index.html" :external-format :utf-8 ))) `( 200 ( :content-type "text/html; charset=utf-8" ) ( ,html ) ) )) Lisp ç³»èšèªã§ã¯ãã®SåŒãšåŒã°ãã (颿°å åŒæ°1 åŒæ°2 ... åŒæ°N) ãšãããããªèšæ³ã§ããã°ã©ã èªäœãèšè¿°ããŸãããã®ããŒã¿ãããã°ã©ã æ¬äœãå
šãŠãã®SåŒã§è¡šçŸããããšã§ãéåžžã«åŒ·åãªãã¯ããäœãããããã®ãç¹åŸŽãšãªã£ãŠããŸãã å®è£
ããããã§èŠåŽãããšãã Common Lisp ãKiroã§å©çšããã«ããã£ãŠèŠåŽããç¹ã工倫ããç¹ãããã€ããããŸããã Common Lisp ãKiroã§å©çšããããã®æŽå ãŸãã Common Lisp ãKiroãå©çšã§ããããã«ããããã®æŽåã§ããäŸãã°ã Common Lisp ã«ã¯æšæºã§ãããã¬ãå®è£
ãããŠããããšã©ãŒçºçæãªã©ã«ã¯èªåçã«ãããã¬ãèµ·åããŸãã Common Lisp ã§åºã䜿ãããŠããéçºç°å¢ã®SLIMEã§ã¯ããã䟿å©ã«å©çšããããšãã§ããŸãããããããã®æ©èœã¯Kiroãªã©ã®AIã«ãšã£ãŠã¯å¯Ÿè©±çãªæäœãå¿
èŠã«ãªã£ãŠããŸãAIã®æäœãé»å®³ããŠããŸããŸãã ãŸããASDF(Another System Definition Facility)ãšãã Common Lisp ã® ããã¡ã¯ãã¹ã¿ã³ããŒã ãªãã«ã管çã·ã¹ãã ããããŸãããããäºåã«å®çŸ©ãè¡ã£ãŠããã²ãªåœ¢ã®ã¢ããªã±ãŒã·ã§ã³ãåäœãããããªç¶æ
ãŸã§æŽåãè¡ããŸããããã®äžã§ãmakeãå©çšããŠåžžã«ãããã¬ãèµ·åããªããªãã·ã§ã³ãä»ããŠèµ·åããããã«ããKiroãããmakeçµç±ã§å®è¡ãããããªåœ¢ã«ããŸããã æçµçã«ã¯ä»¥äžã®ãã㪠Makefile ã®ãšã³ããªãšãªããŸããã --disable-debugger ãå®è¡æã«åŒæ°ã§æž¡ããASDFã䜿ã£ãŠKabotanããã«ããå®è¡ãã圢ã«ãªã£ãŠããŸã( ql:quickload ãASDFãå
éšã§åŒã¶ä»çµã¿ã«ãªã£ãŠããŸã)ã ROS = ros LISP_IMPL = sbcl SYSTEM = kabotan TEST_SYSTEM = kabotan-test run: $(ROS) -L $(LISP_IMPL) run -- \ --disable-debugger \ --eval '(ql:quickload :$(SYSTEM))' \ --eval '(uiop:quit (kabotan:main))' Server Sent Eventãžã®å¯Ÿå¿ Kiroã§ã®ã¢ããªã±ãŒã·ã§ã³éçºã«ãããŠãServer Sent Event(SSE)ã«å¯Ÿå¿ãããã®ã«èŠåŽããŸãããSSEã¯ãµãŒãããã¯ã©ã€ã¢ã³ããžãªã¢ã«ã¿ã€ã ã«ããŒã¿ãéä¿¡ããããã®æè¡ã§ãããLLMã®å¿çããªã¢ã«ã¿ã€ã ã«åãåãããã«å¿
èŠã§ããã ãã©ãŠã¶-Kabotanéã®SSEå¯Ÿå¿ Common Lisp ã®Webãã¬ãŒã ã¯ãŒã¯ã§ããclackãhunchentootã¯çŽæ¥ãã®SSEããµããŒãããŠããããç¬èªã«å®è£
ããå¿
èŠããããŸãããããã¯clackã®ãœã±ãããçŽæ¥æäœããæ©èœãå©çšããŠãSSEã«å¯Ÿå¿ãããããšãã§ããŸããã Kabotan-llama.cppéã®SSEå¯Ÿå¿ llma.cppã®ãµãŒãã«ãšã£ãŠKabotanã¯SSEã®ã¯ã©ã€ã¢ã³ããšããŠæ¯ãèãå¿
èŠããããŸãã ããã Common Lisp ã®HTTPã¯ã©ã€ã¢ã³ãã©ã€ãã©ãªã®dexadorãå©çšããŠç¬èªã«å®è£
ããå¿
èŠããããŸãããdexadorã¯éä¿¡ã«å©çšããŠãããœã±ãããæ±ãããšãã§ãããããæäœããããšã§SSEã«å¯Ÿå¿ãããããšãã§ããŸããã ãã®ä»èŠåŽããç¹ HTMXåšãã¯Kiroã«è²ã
æç€ºãåºããªããšããŸã察å¿ã§ããªãããšãããèŠåŽããŸãããHTMXã¯ããã³ããšã³ãã® JavaScript ã³ãŒããæžããããšãã§ããå©ç¹ããããŸãããKiroã«ãã®å©ç¹ãçè§£ããŠãããã®ãé£ããå Žåããããäœãæç€ºãè¡ããªããšããã³ããšã³ãã® JavaScript ã§ã»ãšãã©ã®å®è£
ãè¡ã£ãŠããŸããHTMXã®å©ç¹ããªãæ§æã«ãªã£ãŠããŸãããšããããŸããã ãŸãLLMãå©çšããã¢ããªã±ãŒã·ã§ã³ã¯ãã¹ãã«æéãããã£ãŠããŸããŸãã ãããŠKiroã¯ã³ãã³ãã®å¿çåŸ
ã¡æéãæå€§ã§20åã«ãªã£ãŠããŸãããçšã«ãããè¶
ããŠããŸãããšããããŸããããããªã£ãŠããŸããšãKiroã¯ãã¹ããéäžã§æã¡åã£ããåé¡ããªãã®ã«åé¡ããããšå€å®ããŠç·šéäœæ¥ãè¡ãããšããããéã«åé¡ãããã®ã«åé¡ãªããšå€æããŠããŸã£ããããããšããããéçºå¹çãäœäžããããšããããŸããã Kiroã®äœ¿ãæ¹ã«ã€ã㊠Hackathon å
šäœãéããŠã©ã®ããã«KiroãæŽ»çšããã®ãã«ã€ããŠã玹ä»ããŸãã Kiroã䜿ãããã§éèŠã ãšæãããã€ã³ãã¯ä»¥äžã®ãšããã§ãã SpecãSteeringã®æŽ»çš Hookã®æŽ»çš ãã¹ãã®å·¥å€« ç¹ã«ãSpecãšSteeringã®äœ¿ãåãã¯éèŠã ãšæããŸããã SpecãšããååãèŠããšSpecåŽã«è©³çްãªä»æ§ãæžãã¹ãã ãšèããã¡ã§ãããå®éã«ã¯SteeringåŽã«è©³çްãªä»æ§ãæžãæ¹ã广çã§ãããäŸãã°ã ã¢ãŒããã¯ã㣠ã«é¢ããæç€ºãèšèšäžã®éžæãšãã£ããã®ã¯Steeringã«èšèŒããå®è£
ãé²ãã«ã€ããŠç¶æ³ãå€ãããã³ã«Steeringã¯ãããžã§ã¯ãã®å®éã®ç¶æ³ã衚ãããã«æŽæ°ããå¿
èŠããããŸããã ãããŠãSpecã¯å®éã®å°ããªäœæ¥ãè¡ãããã«å¿
èŠãªæå°éã®ä»æ§ã«çããŠããæ¹ã广çã§ãããåºæ¬çãªåäœã®æŠèŠãäŒããŠãDesign.mdãäœæããŠããããTask.mdãçæããŠãããããã«ããŸãããã€ãŸããSpec㯠ã¹ã¯ã©ã éçºãªã©ã§ãããšããã®ããŠãŒã¶ãŒã¹ããŒãªãŒãã«è¿ã圹å²ãæãããSteeringãã詳现ãªèŠä»¶å®çŸ©æžãããèšèšæžãã«è¿ã圹å²ãæãã圢ã§ãã ããããåæã«çœ®ãã詳现ãªèšèšãªã©ã¯Vibe Codingã§Kiroãšçžè«ããªããé²ãéæSteeringãæŽæ°ããããç°¡åãªãã°ä¿®æ£ãªã©ã¯çŽæ¥ä¿®æ£ãããããŠé²ããŸãããäžå®èŠæš¡ãè¶
ããäœæ¥ã«ãªããããªå Žåã¯SpecãäœæããŠå¯Ÿå¿ããŠãããã ãªãã¡ã¯ã¿ãªã³ã° ãªã©ã®äœæ¥ãSpecãšããŠäœæããŠéæå®æœããããã«ããŸããã 以äžã¯éçºæã®Kiroã®ç»é¢ã®æ§åã§ããAgent Steeringã«è²ã
èšèšäžãæå®ããŠãããäœæ¥ããšã«SpecãäœãéçºããŠãããŸããã ãŸããHookãç©æ¥µçã«æŽ»çšããŸãããHookã䜿ãããšã§ãKiroãçæããã³ãŒãã«å¯ŸããŠèªåçã«è¿œå ã®åŠçãè¡ãããšãã§ããŸããKabotanã§ã¯ lisp ãã¡ã€ã«ãæŽæ°ããããšãã«èªåçã«ãã¹ããå®è¡ãããããã«Hookãèšå®ããŸãããHookã¯äŸ¿å©ãªã®ã§ãããTaskãšããŠKiroãå®è¡ããŠããŸãããHookãå®è¡ããŠããéæ°ããã¿ã¹ã¯ã®çæãåºæ¥ãªããšããæ¬ ç¹ããããŸããã€ãŸããã¿ã¹ã¯ãå®äºãããšKiroãå ±åããŠããã®ã§æ¬¡ã®ã¿ã¹ã¯ãå®è¡ããããšããããHookãåäœããŠããéã¯æ°ããã¿ã¹ã¯ã«çæã§ããªããšããããšã§ãããããKiroã¯çŸåšå®è¡äžã®ã¿ã¹ã¯ãäžæããã€ã³ã¿ãã§ãŒã¹ãåããã¥ããäœçœ®ã«ããã®ã§æåã¯èŠåŽããŸããã 以äžã®UIã§å®è¡äžã®ã¿ã¹ã¯ãªã©ã確èªã§ããŸããã¯ãªãã¯ããŠåããŠè©³çްãåããããã«ãªã£ãŠããŸããåžžã«è¡šç€ºãããŠãããšäŸ¿å©ãªã®ã§ããä»åŸæ¯éæ¹åããŠã»ããã§ããã ãã¹ãã®æžãæ¹ãç°¡å㪠åäœãã¹ã ã§ããã°Kiroã«çæããŠãããããã«ããŠãå®éã®åäœã確èªãããããªç·åãã¹ãã«ã€ããŠã¯çްããæç€ºãåºããŠKiroã«çæããŠãããããã«ããŸãããç·åãã¹ãã§ã¯åãå
¥ãã®ããã®ãã¹ããäœããããªæç€ºãåºãããããããŸãã«å®è¡ãããããªéçšãè¡ããŸãããããã¯æçµçãªåäœã ãã¯ã¡ãããšç¢ºèªããããšããæå³ã§ããã Kiroãè¯ããªã£ãŠããç¹ Kiroãçºè¡šãããŠããæéãçµéããŠããããã®éã«Kiroèªäœãæ¹åãããŠããŸãããä»åã® Hackathon ãéããŠç¹ã«è¯ããªã£ãŠãããšæããç¹ã¯ä»¥äžã®ãšããã§ãã å©çšã§ããã¢ãã«ãå¢ããç¹ã«Claude Sonnet 4.5ãå©çšã§ããããã«ãªããŸãããããã«ãããçæãããã³ãŒãã®å質ãåäžããŠããŸãããŸããå©çšäžã«KiroãGA(General Availability)ã«ãªãQ Developer CLI ãKiro CLI ã«ãªã£ããšããå€åããããŸãããããã«åãããŠã¢ã«ãŠã³ã管çãªã©ãKiroåŽã§è¡ãããšãå¯èœã«ãªãããã䜿ãããããªã£ãŠããŸãããç¹ã«äžéã«éããå Žåã«ã远å ã§èª²éãè¡ãããšã§å©çšãå¯èœã«ãªãã®ã¯ãšãŠã䟿å©ã«ãªã£ãç¹ã§ããQ Developerã詊ããŠããããã¯äžéã«éãããšå©çšã§ããªããªã£ãŠããŸããéçºãäžæãããŠããŸãããšããããŸãããæ°èŠã¢ã«ãŠã³ãããã®éœåºŠçºè¡ãããšããææ®µãããã®ã§ãããäŒç€Ÿã®ã¢ã«ãŠã³ãã§å©çšããŠããå Žåã¯é£ããå Žåãããã®ã§ã远å 課éã§å¯Ÿå¿ã§ããã®ã¯äŸ¿å©ã§ãã ãŸããããããã£ããŒã¹ã®ãã¹ããçæã§ããããã«ãªããŸããã以å㯠åäœãã¹ã ãªã©ã®å
·äœçãªå€ã䜿ã£ããã¹ããäžå¿ã§ããããä»åã¯ããããã£ããŒã¹ã®ãã¹ããçæããããã«æç€ºãåºãããšã§ãããåºç¯å²ãªåäœç¢ºèªãå¯èœã«ãªããŸããåå
¥ãã¹ããªã©ã§ã¯ç¹ã«æå¹ã ãšæããŸããã ãŸãšã Kiroã® Hackathon ã€ãã³ãã§ããKiroweenã«åå ãã Common Lisp ã䜿ã£ãHTMX+LLMã¢ããªã±ãŒã·ã§ã³ã§ããKabotanãéçºããŸãããKiroãæŽ»çšããããšã§ãå¹ççã«éçºãé²ããããšãã§ãã Common Lisp ã§ãè¿ä»£çãªã¢ããªã±ãŒã·ã§ã³ã®å®è£
ãå¯èœã§ããããšã瀺ãããšæããŠããŸãã ãŸãKiroã¯èšèªã®éå®ãªãå©çšã§ãããšããããšã å
¬åŒããã¥ã¡ã³ã ã§èšèŒãããŠããŸãã Common Lisp ã§ãåé¡ãªã察å¿åºæ¥ãŸãããæ¡çšããæ©äŒãå°ãªãèšèªãå«ããŠè²ã
ãªèšèªã§ã¢ããªã±ãŒã·ã§ã³éçºå¯èœã§ããããšã確èªã§ããŸããã Kiroèªäœãæ¹åãããŠããããã䜿ãããããªã£ãŠããŸãããä»åŸãKiroãæŽ»çšããŠæ§ã
ãªã¢ããªã±ãŒã·ã§ã³éçºã«ææŠããŠãããããšèããŠããŸãã 以äžãKiroweenåå ã¬ããŒãã§ããã ç§ãã¡ã¯äžç·ã«åããŠããã仲éãåéããŠããŸãïŒ é»éç·ç ãã£ãªã¢æ¡çšãµã€ã é»éç·ç æ°åæ¡çšãµã€ã å·çïŒ @yamashita.tsuyoshi ã¬ãã¥ãŒïŒ Ishizawa Kento (@kent) ïŒ Shodo ã§å·çãããŸãã ïŒ
æ¬èšäºã¯ã Property-Based Testing Caught a Security Bug I Never Would Have Found ãã翻蚳ãããã®ã§ãã ã¿ãŒã²ããåã©ã³ãã ãã¹ããå®éã®ã»ãã¥ãªãã£è匱æ§ãçºèŠãããšã ã»ãã¥ãªãã£è匱æ§ã¯ãç§ãã¡ããã¹ãããããšæããªãã³ãŒãã®é
ã«é ããŠããããšããããããŸããæ£åžžç³»ãã¹ããæžããæ³åã§ããããã€ãã®å¢çå€ã±ãŒã¹ããã¹ãããŸãããèããããªãå
¥åã«ã€ããŠã¯ã©ãã§ããããïŒ LLM ãããã©ã«ãã§ãããã®ã·ããªãªãåŠçããŠãããšä»®å®ããããšãå€ãã§ãããLLM ãçæããã³ãŒãã人éãæžããã³ãŒããšåæ§ã«ãã°ãè匱æ§ãå«ãå¯èœæ§ããããŸãããŠãŒã¶ãŒãã¢ããªã±ãŒã·ã§ã³ã«æªæã®ããæååãå
¥åãããã©ããªãã§ããããïŒ ããã¯ãKiro ã® ææ°ã® GA æ©èœ ã䜿çšã㊠AI ã§ãã£ããã¢ããªã±ãŒã·ã§ã³çšã®ã¹ãã¬ãŒãžãµãŒãã¹ãæ§ç¯ãããã¹ããè¡ã£ããšãã«èµ·ãã£ãããšã§ãã 仿§é§åéçºïŒSDDïŒã¯ãŒã¯ãã㌠ã«åŸã£ãŠãKiro ã¯èŠä»¶ãæ
éã«å®çŸ©ãããã¹ãå¯èœãªããããã£ãæœåºããAPI ããŒã®ä¿åãšååŸã®ããã®äžèŠåçŽãªã³ãŒããå®è£
ããŸãããå®è£
ã¯å
å®ã«èŠããŸãããã³ãŒãã¬ãã¥ãŒã§ãæ¿èªãããã§ããããåŸæ¥ã®åäœãã¹ããééããã§ãããã ããããããããã£ããŒã¹ãã¹ãã® 75 åç®ã®å埩ã§ãäºæããªãããšãèµ·ãããŸãããã©ãŠã³ãããªããã±ãŒã¹ã®ããããã£ãã¹ãå
šäœã倱æããã®ã§ããåçŽãªä¿åãšååŸæäœã§ããã¯ããã代ããã« JavaScript ãããã¿ã€ãã®èª€ã£ãåŠçãé²åããŸãããããã¯ãæ©æã«æ¬ é¥ãæé€ããããæ³šæããªããšãå°æ¥çã«ã»ãã¥ãªãã£åé¡ã«ã€ãªããå¯èœæ§ããããã°ã§ãã ãã®æçš¿ã§ã¯ãããããã£ããŒã¹ãã¹ãïŒPBTïŒã人éã®çŽæãåŸæ¥ã®ãã¹ãææ³ã§ã¯èŠéãããã§ãããã»ãã¥ãªãã£ãã°ãã©ã®ããã«çºèŠãããã®ã¹ããŒãªãŒã玹ä»ããŸãã以äžã«ã€ããŠèª¬æããŸãã Kiro ãå®çŸ©ãã仿§ãšãããã㣠éå€§ãªæ¬ é¥ãå«ãã§ããäžèŠç¡å®³ãªå®è£
PBT ã®å
¥å空éã®äœç³»çãªæ¢çŽ¢ãè匱æ§ãã©ã®ããã«çºèŠããã è匱æ§ã«å¯ŸåŠããä¿®æ£ ãããå®å
šãªãœãããŠã§ã¢æ§ç¯ã«ãšã£ãŠãªãéèŠãªã®ã ããã¯åãªãçè«çãªæŒç¿ã§ã¯ãããŸãããèªåãã¹ãæè¡ããã»ãã¥ãªãã£ç ç©¶è
ãå€ãç ããªããããšããžã±ãŒã¹ããæ¬çªç°å¢ã«å°éããåã«çºèŠã§ããããšã®å®äŸã§ãã èæ¯ äžéšã®é¡§å®¢ãšã¢ããªã±ãŒã·ã§ã³ã®æ§ç¯ã«åãçµã¿ã仿§ã®ããã³ãããæ€èšããéãKiro ã¯ãŠãŒã¶ãŒããŒã¿ããã©ãŠã¶ã® localStorage ã«ä¿åãããã£ããã¢ããªã±ãŒã·ã§ã³çšã®ã¹ãã¬ãŒãžã·ã¹ãã ãå®è£
ããŠããŸãããäž»èŠãªæ©èœã®äžã€ã¯ãç°ãªã LLM ãããã€ããŒïŒOpenAIãAnthropic ãªã©ïŒã® API ããŒãä¿åããããšã§ããããŠãŒã¶ãŒã¯ãããã€ããŒåãããŒãšã㊠API ããŒãä¿åã§ããŸãããã®ãªããžã§ã¯ãã¯ä»¥äžã®ãã㪠API ãæã¡ãŸãã storageService.saveApiKey("openai", "sk-abc123..."); storageService.saveApiKey("anthropic", "sk-ant-xyz..."); Kiro 㯠SDD ã«åŸã£ãŠä»¥äžã®èŠä»¶ãçå®ããŸããã ### èŠä»¶ 6 **ãŠãŒã¶ãŒã¹ããŒãªãŒ:** ãŠãŒã¶ãŒãšããŠãç°ãªã LLM ãããã€ããŒã® API ããŒãèšå®ããããããããããšã§ãèªåã®ã¢ã«ãŠã³ãã䜿çšããŠã³ã¹ãã管çã§ããã #### åãå
¥ãåºæº 1. ãŠãŒã¶ãŒãèšå®ãéãããšãããã£ããã¢ããªã±ãŒã·ã§ã³ã¯å LLM ãããã€ããŒã® API ããŒå
¥åãã£ãŒã«ãã衚瀺ãã 2. ãŠãŒã¶ãŒã API ããŒãä¿åãããšãããã£ããã¢ããªã±ãŒã·ã§ã³ã¯ãããããŒã«ã«ã¹ãã¬ãŒãžã«å®å
šã«ä¿åãã 3. API ããŒãç¡å¹ãŸãã¯æ¬ èœããŠããå Žåããã£ããã¢ããªã±ãŒã·ã§ã³ã¯æç¢ºãªãšã©ãŒã¡ãã»ãŒãžã衚瀺ããã¡ãã»ãŒãžéä¿¡ãé²ã 4. ãã£ããã¢ããªã±ãŒã·ã§ã³ã¯ã»ãã¥ãªãã£ã®ãã UI ã§ API ããŒå€ããã¹ã¯ãã 5. ãŠãŒã¶ãŒã API ããŒãåé€ãããšãããã£ããã¢ããªã±ãŒã·ã§ã³ã¯ãã® LLM ãããã€ããŒãç¡å¹ã«ãã åãå
¥ãåºæº 2 ã«ã€ããŠè©³ããèŠãŠã¿ãŸããããKiro ã¯ãããéèŠãªæ£ç¢ºæ§ããããã£ãšããŠéžæããŸããã **ãããã㣠19: API ããŒã¹ãã¬ãŒãžã®ã©ãŠã³ãããªãã** *ä»»æã®* ãããã€ããŒã«ä¿åããã API ããŒã«ã€ããŠãã¹ãã¬ãŒãžããååŸãããšåãããŒå€ãè¿ãããã **æ€èšŒå¯Ÿè±¡: èŠä»¶ 6.2** Kiro ã¯ããããã©ãŠã³ãããªãããããããã£ãšåŒãã§ããŸããã©ãŠã³ãããªããã¯æ£ç¢ºæ§ããããã£ã®äžè¬çãªåœ¢ã§ãä»»æã®å€ããå§ããŠãäžé£ã®æäœãå®è¡ããåãå€ã§çµãããã®ã§ãããã®å Žåãä»»æã®æååå€ provider ãš key ããå§ããŠä»¥äžãè¡ããŸããã ã¹ãã¬ãŒãžã® provider ã®äžã« key ãä¿å provider ã«é¢é£ä»ããããå€ãååŸ ãããŠãååŸããå€ã¯ key ãšçãããªããã°ãªããŸããããããçã§ãªãå ŽåïŒç°ãªãå€ãååŸããããäŸå€ãçºçãããããå ŽåïŒãæããã«å®è£
ã«äœãåé¡ããããŸãããã®ä»æ§ã¯çŽ æŽãããèŠããã®ã§ãæ¿èªã㊠Kiro ã« API ãå®è£
ããŠããããŸãã LLM 㯠API ã®äžéšãšããŠä»¥äžã®ã³ãŒããçæããŸããã /** * ç¹å®ã®ãããã€ããŒã® API ããŒãä¿å */ saveApiKey(provider: string, apiKey: string): void { try { const apiKeys = this.loadAllApiKeys(); apiKeys[provider] = apiKey; localStorage.setItem( StorageService.API_KEYS_KEY, JSON.stringify(apiKeys) ); } catch (error) { if (error instanceof Error && error.name === 'QuotaExceededError') { throw new Error('ã¹ãã¬ãŒãžã¯ã©ãŒã¿ãè¶
éããŸãããAPI ããŒãä¿åã§ããŸããã'); } throw error; } } ãã®åŸãKiro ã¯ããããã£ããŒã¹ãã¹ãã䜿çšããŠãã®ã³ãŒãããã¹ãããæåŸ
ããããããã£ãå®éã«æãç«ã€ãšãã蚌æ ãåéããŸããããããã㣠19 ããã§ãã¯ããããã«ãKiro 㯠TypeScript çšã® fast-check ã©ã€ãã©ãªã䜿çšããŠä»¥äžã®ãã¹ããæžããŸããã describe('ãããã㣠19: API ããŒã¹ãã¬ãŒãžã®ã©ãŠã³ãããªãã', () => { /** * æ©èœ: llm-chat-app, ãããã㣠19: API ããŒã¹ãã¬ãŒãžã®ã©ãŠã³ãããªãã * æ€èšŒå¯Ÿè±¡: èŠä»¶ 6.2 * * ãããã€ããŒã«ä¿åãããä»»æã® API ããŒã«ã€ããŠãã¹ãã¬ãŒãžããååŸãããš * åãããŒå€ãè¿ãããã */ it('ä¿åãšèªã¿èŸŒã¿ãµã€ã¯ã«ãéã㊠API ããŒãä¿æãã', () => { fc.assert( fc.property( fc.string({ minLength: 1, maxLength: 100 }), // ãããã€ããŒå fc.string({ minLength: 10, maxLength: 200 }), // API ã㌠(provider, apiKey) => { // åããããã£ãã¹ãå®è¡åã« localStorage ãã¯ãªã¢ global.localStorage.clear(); // API ããŒãä¿å storageService.saveApiKey(provider, apiKey); // èªã¿èŸŒã¿çŽã const loaded = storageService.loadApiKey(provider); // å
ã®å€ãšäžèŽããããšãç¢ºèª expect(loaded).toBe(apiKey); } ), { numRuns: 100 } ); }); Kiro ããã®ãã¹ããå®è¡ãããšãè©Šè¡ #75 ã§å€±æãçºçããŸããïŒKiro ã¯å€±æã Shurinking ãã以äžã®åäŸãå ±åããŸããããããã€ã㌠"__proto__" ãš API ã㌠" " ã äœãèµ·ãã£ãŠããã®ãïŒ ããããã£ããŒã¹ãã¹ãã¯ãããã€ããŒåã«ã©ã³ãã ãªæååãçæãã75 åã®ãã¹ãå®è¡åŸããããã€ããŒåãšããŠæåå "__proto__" ãçæããŸãããããã«ããã以äžã®åäŸã§ãã¹ãã倱æããŸããã åäŸ: ["__proto__"," "] ãããã€ããŒå __proto__ ã§ API ããŒãä¿åããŠããèªã¿èŸŒãããšãããšãå¥åŠãªããšãèµ·ãããæåŸ
ããå€ãååŸã§ããŸãããKiro 㯠Shurinking ã䜿çšããŠæå°åäŸãæç€ºããŠåé¡ãç¹å®ããåé¡ããäœåãªè©³çްãåãé€ãã®ã«åœ¹ç«ã¡ãŸãããã®å ŽåãapiKey æååããžã§ãã¬ãŒã¿ãŒã§èš±å¯ãããæå°ã®æååïŒã¹ããŒã¹ã®ã¿ãå«ãïŒã« Shurinking ããŸããããã¯ãåé¡ãå€ã§ã¯ãªããå¥åŠãªããŒãåé¡ãåŒãèµ·ãããŠããããšã瀺ããŠããŸããJavaScript ã«è©³ããæ¹ãªãããã®ãšã©ãŒã¯ããã«ç®ã«ä»ãã§ãããããããã§ãªãæ¹ã¯èªã¿ç¶ããŠãã ããã ãã㯠JavaScript ããªããžã§ã¯ãã·ã¹ãã ãå®è£
ããæ¹æ³ã®ç¹åŸŽã§ããããäŒçµ±çãªãªããžã§ã¯ãæåããã°ã©ãã³ã°èšèªïŒJavaãPythonãSmallTalk ãªã©ïŒã¯ãã¯ã©ã¹ã®æŠå¿µã䜿çšããŸããåã¯ã©ã¹ã¯ããªããžã§ã¯ãã®æ§ç¯æ¹æ³ãèšè¿°ããç°ãªããªããžã§ã¯ãéã®ç¶æ¿é¢ä¿ãèšè¿°ããã³ãŒãããŒã¹ã®éçã¡ã³ããŒã§ããJavaScript ã¯ããããã¿ã€ãããšåŒã°ãã代æ¿ã¢ãããŒãã䜿çšããŸãããããã¿ã€ãããŒã¹ã®ãªããžã§ã¯ãã·ã¹ãã ã§ã¯ãã¯ã©ã¹ã¯ååšããŸããã代ããã«ããã¹ãŠã®ãªããžã§ã¯ãã«ã¯ãã³ãŒããšããŒã¿ãç¶æ¿ãã¹ã芪ãªããžã§ã¯ããæããããã¿ã€ããšåŒã°ããç¹å¥ãªãã£ãŒã«ããå«ãŸããŠããŸããããã«ãããç¶æ¿é¢ä¿ãåçã«èšå®ã§ããŸããJavaScript ã§ã¯ããã®ãããã¿ã€ã㯠__proto__ ãã£ãŒã«ãã«ååšããŸãããã£ãŒã«ããæååã«èšå®ããããšãããšããJavaScript ãšã³ãžã³ã¯ãããæåŠããå
ã®ãããã¿ã€ãããã®ãŸãŸä¿æããŸãããããã«ãããããããã£ãã¹ãã®ç¬¬ 2 ã¹ãããã§ provider ãæ€çŽ¢ãããšãã«ãå
ã®ãããã¿ã€ãïŒç©ºã®ãªããžã§ã¯ãïŒãååŸããããšã«ãªããŸãã ãããã¿ã€ããžã®æžã蟌ã¿ãäŸã®ããã«ç¡å®³ãšããããã§ã¯ãããŸããã provider ãš apiKey ã¯æ»æè
ã®å¶åŸ¡äžã«ãããããæ»æè
ã apiKey ã«æåå以å€ã®å€ãååŸããæ¹æ³ãèŠã€ããå Žåããããã¿ã€ãã«å€ã泚å
¥ã§ãããªããžã§ã¯ãã®ããããã£ããã®ãããªãèªã¿åããæ»æè
å¶åŸ¡ã®å€ãè¿ãå¯èœæ§ããããŸãã ããã¯æªçšå¯èœã§ããããïŒãããã apiKeys ãªããžã§ã¯ãã¯ååã«é·ãååšãããã·ãªã¢ã«ååŸããã«è§£æŸããã JSON.stringify 㯠__proto__ ãã£ãŒã«ããã¹ãããããããšãç¥ã£ãŠããŸãããŸããã°ããŒãã«ãããã¿ã€ãã倿Žããã®ã§ã¯ãªãã apiKeys ã®ãããã¿ã€ãã®ã¿ãäžæžãããŠããŸããããããã³ãŒãã®ãªãã¡ã¯ã¿ãªã³ã°ã«ããããã®æªçšäžå¯èœãªè匱æ§ãããåºç¯å²ãªåœ±é¿ãäžããå¯èœæ§ã®ãããã®ã«å€ããæ°ããã³ãŒããã¹ãå°å
¥ãããå¯èœæ§ããããŸããããããã£ããŒã¹ãã¹ããæäŸãããã¹ãåã¯ããããå³åº§ã«ææããŠãã³ãŒãããŒã¹ã«ãããŠåŸ®åŠãªäžæ£ç¢ºããé£ãããšããžã±ãŒã¹ãå¢ããã®ãé²ãã®ã«åœ¹ç«ã¡ãŸãã Kiro ã¯ãããã©ã®ããã«ãã¹ãããã®ãïŒ ãããã€ããŒå __proto__ ã§ API ããŒãä¿åããŠããèªã¿èŸŒãããšãããšããä¿åãã API ããŒã®ä»£ããã«ç©ºã®ãªããžã§ã¯ã {} ãååŸããŸããããªããããèµ·ãã£ãã®ã§ããããïŒå
éšã§äœãèµ·ãã£ããã«ã€ããŠããå°ãèæ¯ãçè§£ããŸãããã PBT ã®å©ç¹ã®ïŒã€ãšèšãããŠããã®ã¯ãã€ã¢ã¹ã§ããåäœãã¹ãã§ã¯ããã¹ããæžãã人ïŒã¢ãã«ãŸãã¯äººéïŒããšããžã±ãŒã¹ãèæ
®ããããšããŸããããèªåèªèº«ã®å
éšãã€ã¢ã¹ã«ãã£ãŠå¶éãããŠããŸããåãïŒã¢ãã«/人ïŒãå®è£
ãæžããã®ã§ãå®è£
äžã«èããªãã£ããšããžã±ãŒã¹ãæãã€ãã®ã¯å°é£ã ãšèããã®ã劥åœã§ãããã®å Žåãããããã£ããŒã¹ãã¹ãã䜿çšããããšã§ããã¹ããã¬ãŒã ã¯ãŒã¯ãäœã£ã人ãã¡ã®éåç¥ã䜿ããŸãããã®å Žåãäžè¬çãªãã°ã¿ã€ãã®äœç³»çç¥èâãããã»ã¹ã«æ³šå
¥ããŠããŸããïŒ __proto__ ã¯ãfast-check ã³ãã¥ããã£ã®äœè
ã«ãã£ãŠ PBT ãžã§ãã¬ãŒã¿ãŒã«ãšã³ã³ãŒããããäžè¬çãªãã°æååã®äžã€ã§ãïŒããã¹ãããã»ã¹ã«æ³šå
¥ããŠããŸãã ç¶è¡ããåã«æ³šæãã¹ãç¹ã¯ãPBT ã³ãŒãã« { numRuns: 100 } ãããããšã§ããããã¯ããžã§ãã¬ãŒã¿ãŒããã°ãèŠã€ããããšãã 100 åã®å埩ãããããšãæå³ããŸããKiro ã¯ãããããã©ã«ãã«ããŠããŸãããããã°ã©ã ã«æ±ããä¿¡é Œã¬ãã«ã«å¿ããŠããã®å€ãäžãããäžãããã§ããŸããæã«ã¯ãã£ãšå¿
èŠã§ãããå®è£
ã®ãã¹ãã«å°ãæéããããããã100 å以äžã®å
¥åãã¹ããå®è¡ããããã©ãŒãã³ã¹ãéçºã©ã€ããµã€ã¯ã«ã®ãã®æ®µéã§ã¯ãŸã 䟡å€ããªãå ŽåããããŸããè¯ãç¹ã¯ãå¿
èŠã«å¿ããŠãã€ã§ããããäžãããäžãããã§ããããšã§ãã ä¿®æ£ Kiro 㯠MITRE ã®é«å¹æç·©åæŠç¥ ã«åºã¥ã㊠2 ã€ã®é²åŸ¡çãå®è£
ããŸããã 1. å®å
šãªä¿åïŒ saveApiKey å
ïŒ // ãããã¿ã€ãæ±æãé¿ãããã null ãããã¿ã€ããªããžã§ã¯ããäœæ const safeApiKeys = Object.create(null); Object.assign(safeApiKeys, apiKeys); safeApiKeys[provider] = apiKey; Object.create(null) ã§äœæããããªããžã§ã¯ãã«ã¯ãããã¿ã€ããã§ãŒã³ããªãããã __proto__ ã¯åãªãéåžžã®ããããã£ã«ãªããŸãã 2. å®å
šãªååŸïŒ loadApiKey å
ïŒ // hasOwnProperty ã䜿çšããŠããŒãå®å
šã«ãã§ã㯠return Object.prototype.hasOwnProperty.call(apiKeys, provider) ? apiKeys[provider] : null; ãã倧ããªèŠç¹ ãã®ã¹ããŒãªãŒã¯ãKiro ã SDD ã®äžéšãšããŠããããã£ããŒã¹ãã¹ãã䜿çšããçç±ã瀺ããŠããŸãïŒ ããããã£ã¯èŠä»¶ã«çŽçµ â ãä»»æã®ãããã€ããŒåã«ã€ããŠãã©ãŠã³ãããªããããããšããããããã£ã¯ãèŠä»¶ããã®ãŸãŸå€æãããã®ã§ãã ã©ã³ãã çæã¯äºæããªããšããžã±ãŒã¹ãçºèŠ â 人éãš LLM ã¯ããã¹ãããå
¥åã«ã€ããŠãã€ã¢ã¹ãæã£ãŠããŸããã©ã³ãã çæã¯ãã¹ãã±ãŒã¹ã培åºçã«è¿œã蟌ã¿ãŸã å®è¡å¯èœãªä»æ§ â ããããã£ã¯å®è¡ã§ãã仿§ã§ãããã³ãŒãã¯äœããã¹ãããïŒèŠä»¶ïŒãšãã³ãŒãã¯å®éã«ãããåããã®ããïŒãã¹ãïŒã®éã®ã®ã£ãããåããŸãã ã¿ã€ããªãã£ãŒãããã¯ã«ãŒã â ããããã£ã倱æãããšããããã°ã容æã«ããæå°éã®åäŸãååŸããŸããKiro ã¯ããã䜿çšããŠã³ãŒããä¿®æ£ããè¿
éãªå埩ãµã€ã¯ã«ãäœæã§ããŸãã ãã®ãã°ã¯ Kiro ã§ã®å®éã®éçºäžã«çºèŠãããŸãããããããã£ããŒã¹ãã¹ãã¯ã以äžã®æ¹æ³ã§ã¯çºèŠãéåžžã«å°é£ã ã£ãã§ãããã»ãã¥ãªãã£åŒ±ç¹ããã£ããããŸããã æåã³ãŒãã¬ãã¥ãŒ æåã§éžãã äŸã䜿ã£ãåŸæ¥ã®åäœãã¹ã çµ±åãã¹ã
äžäŒ.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 çã®æäœãè£åŽã§é èœããå
žåçãªäŸã§ããã ↩
åç»
該åœããã³ã³ãã³ããèŠã€ãããŸããã§ãã











