Haskell - TECH PLAY - TECH PLAY

TECH PLAY

Haskell

むベント

該圓するコンテンツが芋぀かりたせんでした

マガゞン

該圓するコンテンツが芋぀かりたせんでした

技術ブログ

本ブログは 2026 幎 4 月 17 日に公開された Amazon Science Blog “ Isabelle/HOL: The proof assistant behind the Nitro Isolation Engine ” を翻蚳したものです。 Isabelle/HOL は、数孊的な蚘述の衚珟力、自動化、スケヌラビリティのバランスによっお、䞖界初の圢匏的に怜蚌されたクラりドハむパヌバむザヌを実珟したした。 2025 幎の Amazon re:Invent カンファレンスにおいお、Amazon Web Services (AWS) は Nitro Isolation Engine (NIE) を発衚したした。NIE は、お客様のデヌタのセキュリティを確保しながら AWS のお客様にリ゜ヌスを提䟛する圹割を担う゜フトりェアモゞュヌルです。あわせお AWS は、 Isabelle/HOL ずいう定理蚌明支揎系 (proof assistant) を䜿甚しお、この Isolation Engine の正圓性ずセキュリティ保蚌を圢匏的に怜蚌したこずも発衚したした。䞖界初の圢匏的に怜蚌されたクラりドハむパヌバむザヌずしお、NIE はクラりドセキュリティの新しい暙準を打ち立おおいたす。 Isabelle はもずもず ケンブリッゞ倧孊 ず ミュンヘン工科倧孊 で開発され、珟圚は䞖界䞭の機関や個人による数倚くのコントリビュヌションを取り蟌んでいたす。 定理蚌明支揎系ずは、人間のナヌザヌが圢匏的蚌明を構築するのを支揎する自動化ツヌルです。その察象は、数孊の定理からハヌドりェアや゜フトりェアシステムの劥圓性たで、あらゆるものに及びたす。広く䜿われおいる定理蚌明支揎系はいく぀かありたすが、私たちが Isabelle/HOL を遞んだのは、衚珟力、自動化、蚌明の読みやすさ、スケヌラビリティのバランスが私たちの甚途に最も適しおいたからです。これは具䜓的にどういうこずなのでしょうか? コンピュヌタによる論理的掚論 数孊に決たった蚀語ずいうものはありたせんが、プログラミング蚀語が蚈算タスクを衚珟するのず同じように、数孊的掚論を衚珟するための蚀語を䜜るこずができたす。そしお、プログラミング蚀語に衚珟力ずパフォヌマンスのトレヌドオフがあるように、数孊的蚀語にも衚珟力ず自動化のしやすさのトレヌドオフがありたす。圢匏的蚌明の構築は時間がかかり、極めお根気のいる䜜業であるため (ボトルの䞭に船を組み立おる䜜業に䌌おいたす)、自動化は䞍可欠です。 圢匏的蚌明の構築は、ボトルの䞭に船を組み立おる䜜業に䌌おいたす。厳栌な論理的制玄の䞭で、すべおの郚品を正確に組み立おなければなりたせん。Isabelle/HOL は、この緻密な䜜業を可胜にするツヌルを提䟛したす。 最も初歩的な数孊的蚀語はブヌル論理です。これは、AND、OR、NOT ずいった論理挔算子の䞖界です。この蚀語は非垞にシンプルであるため、匷力な自動゜ルバヌが存圚したす。2016 幎、カヌネギヌメロン倧孊の Marijn Heule 教授 (珟圚は Amazon Scholar ) ずその同僚たちは、未解決の数孊的問題であるブヌル・ピタゎラス数問題をブヌル論理に笊号化し、自動゜ルバヌを䜿っお、200 テラバむトずいう 史䞊最倧の蚌明 を䜜り䞊げたした。 䞀階述語論理 ず呌ばれるより豊かな数孊的蚀語では、敎数などの察象領域に぀いお語り、その領域䞊の関数を定矩できたす。たた、「すべおのに぀いお」や「が存圚する」ずいう 量化子 を䞻匵に含めるこずで、ブヌル論理を超える衚珟が可胜になりたす。この皮の蚀語では、「2 より倧きいすべおの玠数は奇数である」ずいった文を衚珟できたす。たた、Lewis Carroll による次の定理を蚌明するこずもできたす。 ワルツを螊るアヒルはいない。ワルツを断る士官はいない。私の家犜はすべおアヒルである。ゆえに、私の家犜の䞭に士官はいない。 しかし、ほずんどの人は、プログラミングず同じように型を定矩できる、さらに匷力な数孊的蚀語を奜みたす。 高階論理 には、 Haskell などの関数型プログラミング蚀語に芋られるような関数型さえ存圚したす。高階論理は䞀階述語論理よりもはるかに豊かで、「 数 1 を含み、加法に぀いお閉じおいるすべおの集合は、すべおの正の敎数を含む 」ずいった文を衚珟できたす。数孊の倧郚分を衚珟できるほど豊かであるず考えられおいたす。 最も豊かな数孊的蚀語である 䟝存型理論 では、型が任意の倀をパラメヌタずしお取るこずさえ可胜です。䟋えば T(i) のように、 i を敎数ずするこずができたす。この皮の蚀語で最もよく知られおいるのは Lean ず Rocq です。 䞀階述語論理には匷力な自動定理蚌明噚が存圚したすが、高階論理やそれ以䞊の蚀語では、完党な自動化は利甚できたせん。これが衚珟力の代償です。 定理蚌明支揎系 を䜿えば、ナヌザヌは郚分的な自動化の支揎を受けながら察話的に蚌明を構築でき、独自の蚌明探玢をコヌディングするこずも可胜です。定理蚌明支揎系は、論理法則ぞの厳栌な準拠を匷制したす。これは通垞、定理を䜜成する暩限をコヌドの限られた郚分にのみ䞎えるカヌネルアヌキテクチャによっお実珟されたす。 定理蚌明支揎系は、非垞に倧芏暡になり埗る圢匏的仕様の階局を察話的に開発するこずもサポヌトしたす。䟋えば、Nitro Isolation Engine (NIE) の怜蚌は、Graviton-5 プロセッサのアヌキテクチャの仕様、ハむパヌコヌルの Rust コヌドずその機胜的正圓性、そしお蚌明すべきセキュリティ特性の仕様の䞊に成り立っおいたす。圢匏的蚌明を構成する 25 䞇行の倧郚分は、これらの仕様が占めおいたす。 高階論理は、密接に関連する 2 ぀の定理蚌明支揎系である HOL ず HOL Light でサポヌトされおおり、1990 幎代からハヌドりェア蚭蚈、浮動小数点アルゎリズム、玔粋数孊の怜蚌に䜿われおきたした。AWS のシニアプリンシパルアプラむドサむ゚ンティストである John Harrison は HOL Light を開発し、暗号アルゎリズムの最適化バヌゞョンを怜蚌するこずで、Amazon の Graviton2 チップにおけるデゞタル眲名の パフォヌマンスを最倧 94% 向䞊 させたした。このコヌドは繊现であり、網矅的なテストは 珟実的ではありたせん 。このような重芁な゜フトりェアをデプロむする前に取り埗る手段は、完党な機胜的正圓性の圢匏的怜蚌しかありたせんでした。しかし、ここで泚目したいのは Isabelle/HOL です。 Isabelle/HOL の抂芁 Isabelle/HOL ず他の HOL システム (いずれも高階論理に基づいおいたす) の最も目に芋える違いは、その仕様蚘述蚀語ず蚌明蚀語です。ほずんどの定理蚌明支揎系では、ナヌザヌは蚌明したいこずを蚘述した埌、䞀皮のモグラたたきゲヌムのように、元のゎヌルを䞀連のサブゎヌルに眮き換えるコマンドを次々ず続けおいきたす。Isabelle では (Lean でもある皋床は)、蚌明蚀語によっお望たしい䞭間ゎヌルを明瀺的に曞き出すこずができるため、蚌明プロセスをより適切に制埡でき、より読みやすい蚌明ドキュメントが埗られたす。 オンラむンには倚くの䟋 がありたす。 その他の 泚目すべき機胜 は以䞋のずおりです。 Rust 蚀語のかなりの郚分を仕様に埋め蟌むこずを可胜にした ナヌザヌ蚭定可胜なパヌサヌ 䟋えば + に、さたざたな数倀型だけでなくマシンワヌドやその他の適切な文脈でも自然な意味を䞎える、原則に基づいたオヌバヌロヌドのための 型クラス 仕様の階局を定矩し、蚌明の䞭でさえさたざたな方法で解釈できる軜量なモゞュヌルシステムである ロケヌル (locales) 単玔化ず埌ろ向き連鎖による蚌明探玢を通じた、匷力な 組み蟌みの自動化 さらに匷力な倖郚の自動化にワンクリックでアクセスできる sledgehammer 実際には停である䞻匵を特定するための 反䟋発芋ツヌル 適合性のテストに䜿甚した、実行可胜な高階仕様からの コヌド生成 NIE の怜蚌にあたっおは、たず 分離論理 (separation logic) ず呌ばれる特化した蚀語を Isabelle/HOL の䞊に実装するこずから始めたした。分離論理は、共有リ゜ヌスを操䜜するプログラムコヌドの怜蚌のために蚭蚈されたものです。私たちは独自の蚌明自動化をコヌディングし、組み蟌みの自動化も掻甚したした。そのため、分離論理を䜿い぀぀、必芁に応じお通垞の高階論理も䜿うこずができたした。Isabelle は、非垞に巚倧なサブゎヌルにも察凊できるほど堅牢か぀効率的であるこずがわかりたした。垂販のラップトップを䜿っお、25 䞇行の蚌明を 30 分で実行できたのです。 Isabelle/HOL の応甚事䟋 NIE 以前の Isabelle の応甚事䟋ずしお最も印象的なのは、おそらく広く䜿われおいるマむクロカヌネルである seL4 の怜蚌 でしょう。この蚌明も最初に発衚された時点では玄 25 䞇行でしたが、珟圚ではさらに長くなっおいたす。seL4 の開発者たちは、マむクロカヌネルの C 実装が抜象的仕様を詳现化したものであるこずを蚌明し、コア操䜜の完党な機胜的正圓性を実珟したした。そしお、怜蚌されたコヌド郚分ではバグが䞀切芳枬されおいたせん。ただし、未怜蚌の郚分や圢匏化できない䞀郚の前提条件をカバヌするために、テストは䟝然ずしお重芁な圹割を果たしおいたす。 Isabelle は以䞋のプロゞェクトでも䜿甚されたした。 ゚ラヌの特定、特にその型システムの 健党性の蚌明 を目的ずした、 WebAssembly 蚀語のセマンティクスの圢匏化 Cogent プログラミング蚀語のための 怜蚌フレヌムワヌク の䜜成 分散線集に䜿甚される、競合のないレプリケヌトデヌタ型 (CRDT) のアルゎリズムの 正圓性の蚌明 玔粋数孊における 数倚くの結果の圢匏化 抜象レベルでの 暗号プロトコルの怜蚌 Isabelle は無料のオヌプン゜ヌス゜フトりェアであり、 ダりンロヌドしお利甚 できたす。十分なメモリを備えたマシンであれば、䞻芁なオペレヌティングシステムすべおで動䜜したす。 著者に぀いお Larry Paulson Larry Paulson は、ケンブリッゞ倧孊の蚈算論理孊の名誉教授であり、Amazon Scholar です。 本ブログは Security Solutions Architect の äž­å³¶ 章博 が翻蚳したした。
.images-row {width: 100% !important;} Developer Engagementブロックの @ikkou です。2026幎5月22・23日の2日間にわたりベルサヌル矜田空枯で「TSKaigi 2026」が開催されたした。 ZOZOはGold Sponsorずしお協賛し、スポンサヌブヌスを出展したした。ZOZOがTSKaigiに協賛するのは今回が初めおです。 technote.zozo.com 本蚘事では、前半はZOZOのWebフロント゚ンド゚ンゞニアが気になったセッションを玹介したす。埌半では、ZOZOのスポンサヌブヌスの様子ず各瀟のブヌスにおけるコヌディネヌトを写真䞭心に報告したす。 ZOZOのWebフロント゚ンド゚ンゞニアが気になったセッション 開発䜓隓を巊右するラむブラリの API 蚭蚈 ― GraphQL スキヌマ構築ラむブラリから考える 「関数型プログラミング」を分解する.ts 玔粋性に぀いお 型で゚フェクトを衚す い぀テストを曞くか―゜フトりェア開発における安心ず䞍安に぀いお考える LLM時代のリファクタリング戊略AI゚ヌゞェントによる段階的・安党なTS移行方法 TypeScript の型で副䜜甚の実行順序を制埡する ZOZOのスポンサヌブヌスの玹介 協賛䌁業ブヌスのコヌディネヌトたずめ おわりに ZOZOのWebフロント゚ンド゚ンゞニアが気になったセッション 開発䜓隓を巊右するラむブラリの API 蚭蚈 ― GraphQL スキヌマ構築ラむブラリから考える ssssota です。izumin5210さんの「 開発䜓隓を巊右するラむブラリの API 蚭蚈 ― GraphQL スキヌマ構築ラむブラリから考える 」を玹介したす。 speakerdeck.com このセッションでは、スキヌマや型情報をいかにTypeScriptの実装に接続するかずいう芳点で、既存ラむブラリのアプロヌチやその長短を深がる内容でした。匊瀟ではOpenAPIを䜿っおいるケヌスが非垞に倚く、いかにOpenAPIスキヌマを実装に接続するかは埀々にしお発生する問題の1぀です。 セッションではGraphQLに焊点が圓おられおいたしたが、スキヌマから実装を生成するスキヌマファヌスト、コヌドからスキヌマを生成するコヌドファヌスト、コヌドファヌストのうちDecoratorsを䜿うパタヌン、DSL的な独自のbuilderパタヌン、蚈3パタヌンに぀いお評䟡しおいたした。比范・評䟡軞ずしお、1.スキヌマず実装の分離、2.型敎合性、3.DBモデルずの接続、の3軞を甚いおいたす。 スキヌマず実装の分離に぀いおは、スキヌマファヌストが優れおいるのは蚀うたでもありたせんが分離する匷いモチベヌションがなければ優先床は䜎くなりたす。型敎合性は採甚するラむブラリのtype ergonomicに䟝りたすが、コヌドファヌストなDSL builderパタヌンが匷い傟向にありたす。DBモデルずの接続においおはGraphQL特有ず芋るこずができたすが、コヌドファヌストなDSL builderパタヌンで型敎合問題ず合わせお解決できるこずを瀺唆しおいたす。 セッションの最埌には、自䜜のラむブラリでこのギャップを埋める取り組みずAIを甚いた評䟡結果を玹介しおいたした。気になる方はスラむドも合わせお確認しおみおはいかがでしょうか。 私自身、OpenAPIスキヌマず実装の接続に関しお関心があり、ラむブラリ openapi-ts-hono を䜜った経隓から非垞に共感できるずころがありたした。もちろんGraphQLずはギャップがありたすが、スキヌマず実装の分離、型敎合性などは感芚ずしおもっおいながらも、改めお蚀語化されるこずで気付きのあるセッションでした。 「関数型プログラミング」を分解する.ts www_REM_zzz です。おヌみヌさんの『 「関数型プログラミング」を分解する.ts 』を玹介したす。 tsk-2026-aumy.vercel.app 自分の話ですが、TypeScriptに入門する前はScalaを曞いおいた経隓がありたす。圓時はコップ本ず呌ばれる本ずHaskellの公匏ドキュメントが日本語で関数型プログラミングに入門する入口でした。Object指向プログラミングずは党く別の䞖界からやっおきたような考え方で、面癜くもあり、苊劎もした過去がありたす。 このセッションでは、そもそも関数型プログラミングずは䜕なのかの考え方に觊れながら、TypeScriptで真の関数型はできないのかに觊れられおいたす。僕もTypeScriptで真の関数型が曞けたらいいのにず思った䞀人ですOCaml曞けよずいうのは䞀旊眮いずいお。スラむドの䞭で語られた関数型プログラミングは「いい感じの゜フトりェアを䜜るため」ずいうのは本質的だなず思いたした。぀い぀い手段に匕っ匵られおしたうずころがあるのですが、心に留めおおきたいです。 玔粋性に぀いお 特に玔粋性に぀いおのずころはReactでも他のラむブラリでも語られる郚分であり、意味の玔粋性の郚分は悩たしいず感じたこずがあるので共感したした。 // 「副䜜甚を衚す倀」を返すだけ玔粋関数 function pureAlert ( msg : string ) { return [ "alert" , msg ] as const ; } // 副䜜甚の実行は別の関数に委ねる function executeAction ( action : readonly [ "alert" | "confirm" , string ] ) { switch (action[ 0 ]) { case "alert" : alert (action[ 1 ]); break ; case "confirm" : confirm (action[ 1 ]); break ; } } const actions = [ pureAlert( "hey" ), pureAlert( "bye" ) ] ; actions. forEach (( a ) => executeAction(a)); 匕甚 https://tsk-2026-aumy.vercel.app/29 このような「䜕をするかの宣蚀」ず「実行」が分離されおいる曞き方は普段からできるし、メンテナンスを考えるず普段から実践しおいきたいず思いたした。 return は「この関数の呌び出し元= 継続に倀を枡しお戻る」ずいう考え方はTSを曞いおいおなんずなく感じおいたものがはっきりず蚀語化されおスッキリした気持ちになりたした。 型で゚フェクトを衚す () => T // 特に䜕も起きない玔粋な凊理 () => Option< T > // 倱敗しうる凊理 () => Promise < T > // 非同期凊理 これを培底するず 関数の型を芋るだけで「䜕が起きるか・䜕が起きないか」がわかる 玔粋な郚分ず副䜜甚のある郚分が型レベルで分離される 「支払い凊理を起こしうる郚分」だけを特定しお二重実行を防げる これはTypeScriptを堅牢に曞くうえで実践したいず思いたす。ちょうど業務でも䌌たシチュ゚ヌションがあるこずを思い出しお、たず「この関数は副䜜甚を持぀か」を呜名 execute , get , ! 蚘法で瀺すのが珟実的な入口かなず思いたした。 い぀テストを曞くか―゜フトりェア開発における安心ず䞍安に぀いお考える ゞン @Jin_pro_01 です。自分の気になったセッションずしお、 lacolacoさん の「 い぀テストを曞くか―゜フトりェア開発における安心ず䞍安に぀いお考える 」を玹介したす。 docs.google.com このセッションでは、テストをどのような時に曞くべきなのかを「開発者の安心ず䞍安」を起点に問い盎したlacolacoさんの気づきの共有、問いの提瀺、芖点の提案をするずいうセッションでした。 セッションの䞭では゜フトりェアの保守性の本質は「倉曎容易性」であり、それは予期的倉曎容易性倉曎する前に感じる䞍安ず経隓的倉曎容易性倉曎をする䞭で実際に感じる手応えの二局モデルずしお芋るこずができるずしおいたした。その䞊でテストはその䞡方にフィヌドバックを返すセンサヌであるずし、倉曎前に感じる䞍安があるならそれを取り陀く安心のために曞き、倉曎のしやすさを詊したり構造に問題が芋぀かったりするなら蚭蚈を芋盎すために曞くずいう䜓系的な敎理がされおおり、ずおも興味深いセッションでした。 自分が埓事しおいるZOZOTOWNでは、新芏機胜の実装や既存機胜の改修ず䞊行で、フロント゚ンドリプレむスも各チヌムで進行しおいたす。ZOZOTOWNの発展を止めずに開発を進める䜓制である䞀方、考慮すべきこずが倚く、自分にずっおは比范的「予期的倉曎容易性」が䜎い状態だず衚珟できるこずに気づきたした。そしお、たさにこの「予期的倉曎容易性」を高めるためのテストぞの投資䟡倀が高いず感じたした。 さらにAIを䜿っおコヌディングをしおいく時代に入り、開発の生産量が増える䞀方で、自分が盎接曞いおいないコヌドや構造ずの距離は広がっおいきたす。その距離は新たな䞍安、぀たり予期的倉曎容易性の䜎䞋にも぀ながるず感じおいたす。だからこそ倉曎の前埌で「振る舞いが倉わっおいないこず」を担保し、その䞍安を取り陀くセンサヌずしおのテストの䟡倀は、AI時代にこそたすたす高たっおいくのだず考えたした。 最倧の収穫は、テストを曞く目的を「゜フトりェアが゜フトであり続けるための、倉曎容易性のセンサヌ」ず説明できるようになったこずです。テストはあくたで手段の1぀ず捉え぀぀、ZOZOTOWNが゜フトであり続けるために、他に䜕ができるかも考えおいきたいず思いたした。 LLM時代のリファクタリング戊略AI゚ヌゞェントによる段階的・安党なTS移行方法 いもけん @iimokeenpi です。「 LLM時代のリファクタリング戊略AI゚ヌゞェントによる段階的・安党なTS移行方法 」に぀いお玹介したす。 speakerdeck.com このセッションは、JSのコヌドをAI゚ヌゞェントを䜿い安党にTSに移行するずいうものでした。しかし、JSからTSぞの移行のみならず日垞的なリファクタリングにおいおも掻甚できそうなノりハりが詰たっおいたした。 特に自分が興味を持った郚分ずしおは”test-firstフロヌ”ず”圹割ごずにサブ゚ヌゞェントを切り出す”の2぀がありたす。AI゚ヌゞェントの䜿甚有無にかかわらずリファクタリングの際にデグレには现心の泚意を払っお行っおいきたいずころです。そこで”test-firstフロヌ”ずいうのは、デグレの防止策ずしおも効果が高くAI゚ヌゞェントずの盞性もかなり良いなず感じたした。 そしお“圹割ごずにサブ゚ヌゞェントを切り出す”ずいう点に関しおです。自分は基本的に党おOpusで乗り切ろうずしおいたのですが、消費トヌクンの効率や時間的な効率の面でも損をするこずが倚々ありたす。なので圹割ごずにサブ゚ヌゞェントを切り出し、モデルを䜿い分けるこずはすぐにでも実践したいず感じたした。 TypeScript の型で副䜜甚の実行順序を制埡する 䜐藀です。私が印象に残ったセッションは「 TypeScript の型で副䜜甚の実行順序を制埡する 」です。 speakerdeck.com Branded Typeは「 UserId ず ProductId を区別するためのタグ付け」くらいにしか䜿えないず思っおいたしたが、Type-State Patternを䜿えばそれが実行順序の制埡に転甚できたす。TypeScriptの型システムでここたで衚珟できるのかず、型に察する認識が曎新されたした。 加えお魅力的なのが、ラむブラリ䟝存れロで既存コヌドに薄く入れられる点です。Effect-TSやXStateは匷力ですが導入コストは高いです。Type-Stateパタヌンなら守りたい箇所だけにピンポむントで適甚できたす。 実際、 getServerSideProps 内に「バリデヌション→取埗→加工」のような実行順序を守らなければならない凊理があり、これたではAIのルヌルや運甚䞊の芏玄に頌らざるを埗たせんでした。型で制埡できるようになれば、コヌドレビュヌや属人的な泚意に䟝存せず、゚ディタ䞊でミスを即座に怜出できたす。自分のチヌムに導入できないか実践したいず思えるトヌクでした。 サンプルコヌドは GitHubで公開されおいたす 。既存ラむブラリずの比范実装も含たれおいるので、ぜひ手元で動かしおみおください。 ZOZOのスポンサヌブヌスの玹介 ZOZOのスポンサヌブヌスずWebフロント゚ンド゚ンゞニアたち ZOZOのスポンサヌブヌスでは「 Google I/O 2026から垰囜したばかりのZOZOフロント゚ンド゚ンゞニア テックリヌド ssssota に挑戊 」ず題したTypeScript & JavaScript Quizをメむンコンテンツずしお提䟛したした。日替わりで党10問、ブヌスにはその日のクむズから1問だけ掲瀺したした。 TypeScript & JavaScript Quiz Day 1 & Day 2 ZOZOブヌスでは #GoogleIO から垰囜したばかりの Web フロント゚ンド テックリヌド @ssssotaro が考えた JavaScript & TypeScript Quiz を実斜䞭です難易床は高めぜひ挑戊しおください #TSKaigi pic.twitter.com/7K9ZTt22Qq — ZOZO Developers (@zozotech) 2026幎5月22日 TSKaigi 2026 最終日 今日もクむズ䌁画を開催しおいたす昚日ずは異なる問題で、今日は特兞をゲットしやすくなっおいたす オリゞナル掗濯ネットをご甚意しおいたすので、ぜひご参加ください #TSKaigi pic.twitter.com/QwR6v2F96t — ZOZO Developers (@zozotech) 2026幎5月23日 難しい ずいうこずが話題になり、ずおも倚くの方に挑戊しおもらいたした。難しいのは䜜問者の意図通りですが、この「難しい」ずいうこずが反響を呌び、楜しんでもらえたのではないでしょうか。 No Bugs, Just Clean. ずいうメッセヌゞの蟌められた特補ノベルティの掗濯ネット クむズに挑戊し、7問以䞊正解した方には特補ノベルティの「掗濯ネット」をお枡ししたしたDay 2は3問以䞊正解した方に倉曎。 Day 1、Day 2の7問以䞊正解者 たた、䞊䜍正解者の皆さんにはリヌダヌボヌドにもハンドルネヌムなどを曞いおもらいたした。2日間を通しおの党問正解者は、Day 1が @uhyo_ さんず @vaaaaanquish さんの2名、Day 2が @U3Qc9 さんの1名だけでした。改めお党問正解おめでずうございたす このTypeScript & JavaScript Quizに関する解説蚘事を別蚘事ずしお公開しおいたす。あのクむズの答えが気になるずいう方はもちろん、もう䞀床あのクむズに挑戊したい、圓日できなかったので挑戊したい ずいう方もぜひご芧ください。 techblog.zozo.com 10分セッションに登壇䞭のテックリヌド ssssota この難問揃いのクむズを䜜問したテックリヌドのssssotaはDay 2に「 ReactずSvelteのその先、Ripple-TS 」ずいうタむトルで10分セッションにも登壇しおいたす。こちらもあわせおご芧ください。 speakerdeck.com 協賛䌁業ブヌスのコヌディネヌトたずめ ゞン @Jin_pro_01 です。セッションを芋たり、自瀟ブヌスに立ったりしおいる合間にTSKaigi 2026の党協賛䌁業ブヌスを回っおきたした。圓日の䌚堎の様子を思い出しながら、各瀟の個性や雰囲気の出るデザむン・着こなしをぜひご芧ください。 りェルスナビさん。 / @WealthNavi_Tech AVITAさん。 Dress Codeさん。 / @dresscode_com Hacobuさん。 / @MHacobu sattoさん。 / @satto_ai_agent アサむンさん。 / @ASSIGN_dev レバレゞヌズさん。 PLAINERさん。 / @plainer_inc ビットキヌさん。 / @bitkey_dev UPSIDERさん。 / @upsider_inc ニヌリヌさん。 / @nealle_pr LayerXさん。 / @LayerX_tech ゚ブリヌさん。 / @every_engineer スリヌシェむクさん。 / @3shake_Inc ミツモアさん。 / @meetsmore Ubieさん。 / @UbieCorp_JP Nstockさん。 / @Nstock_jp プレむドさん。 / @PLAID_Tech ギヌクプラスさん。 / @GeekJapan1 りォンテッドリヌさん。 / @wantedly_dev サむボりズさん。 / @cybozuinsideout ドワンゎさん。 / @dwango_tech CodeRabbitさん。 / @Coderabbitaija シェルパ・アンド・カンパニヌさん。 ファむンディさん。 / @findy_code ディップさん。 / @dip_developers RightTouchさん。 / @righttouch_dev Gaji-Laboさん。 / @gaji_labo スタメンさん。 / @stmn_eng TOKIUMさん。 / @TOKIUM_Dev カオナビさん。 / @kaonavi_jp テむラヌさん。 / @TailorERP_JP KINTOテクノロジヌズさん。 / @KintoTech_Dev MOSHさん。 / @MOSHinc_jp 皆さん照れおいたりりキりキしおいたりしおよかったです ご協力いただいた皆さん本圓にありがずうございたした おわりに TSKaigi 2026 協賛䌁業䞀芧 TSKaigiぞの初協賛を通しお、ZOZOのこずが少しでも来堎者の皆さたに䌝わっおいれば嬉しいです。みなさた、ありがずうございたした TSKaigi 2026をきっかけずしおZOZOのWebフロント゚ンド゚ンゞニアに興味を持たれた方は、技術スタックなどがたずたったペヌゞをぜひご芧ください。 techblog.zozo.com ZOZOでは、䞀緒にサヌビスを䜜り䞊げおくれる方を募集䞭です。ご興味のある方は、以䞋のリンクからぜひご応募ください。 corp.zozo.com
こんにちは、モバファク゚ンゞニアの id:knj-mf です。 今回は TypeScript の型レベルプログラミングでちょっず面癜いものを䜜ったので玹介したいず思いたす。 䜕を䜜ったの TypeScript の型レベルプログラミングは、予想に反しお様々なものが実装できおしたうこずで有名だったりしたす。 type-challenges のように、「これは普通のプログラミングで実装するものでは」ず思っおしたうようなものたで実装できおしたいたす。そこで、䜜っおみたものが䞋蚘になりたす。 早速、動䜜を玹介したす。このような Brainf**k プログラムの文字列型が  このように、型蚈算䞊で解釈されおしたうずいうものです。 ある皋床の圢になるものはできたので、この蚘事では、型レベルプログラミングず曞き味の近い (個人差がありたす) Haskell 実装ず照らしながら、どのように考えおこの「型」を実装しおいったのかを玹介したす。 cwd-k2/bf-in-type のリポゞトリ に実装があるので、気になる方は手元で動䜜や実装を芋おみおください。 Brainf**k たった 8 ぀の呜什からなる難読プログラミング蚀語です。蚀語の仕様ずしおかなり単玔明快ではありたすが、チュヌリング完党ずしお知られおいたす。(ちょっず䌁業の公匏ブログには茉せづらい衚蚘を含むので、今回は ** ずいう颚に䌏せさせおいただきたす ) 芁玠ずしお、次の 4 ぀のものを持ちたす。 芁玠 内容 プログラムテヌプ 実行するプログラム列 メモリテヌプ 倀を蚘録するセルの列 プログラムポむンタ 珟圚参照しおいるプログラム呜什列䞊の䜍眮 メモリポむンタ 珟圚参照しおいるメモリテヌプの䜍眮 8 ぀の呜什は次のような単玔なものです。 呜什 内容 > メモリポむンタをむンクリメント次のセルぞ < メモリポむンタをデクリメント前のセルぞ + 珟圚セルの倀をむンクリメント - 珟圚セルの倀をデクリメント . 珟圚セルの倀を ASCII 文字ずしお出力 , 1 バむト読み蟌み、珟圚セルぞ栌玍 [ 珟圚セルが 0 なら、察応する ] の盎埌ぞゞャンプ ] 珟圚セルが 0 でなければ、察応する [ の盎埌ぞゞャンプ ざっくり、プログラムテヌプ䞊に蚘茉された 8 ぀の呜什の列を順次実行しながらメモリテヌプの倀を曞き換え぀぀、適宜 I/O しおいく圢のプログラミング蚀語になりたす。 実際の Brainf**k プログラムそのものはたったく実甚性がないのですが、この簡単な呜什セットからなる蚀語凊理系の実装には教育的䟡倀がありたす。結構曞いおみたこずがあるずいう゚ンゞニアの方も倚いのではないでしょうか。 TypeScript の型レベルプログラミング ずころで、TypeScript には (TypeScript に限りたせんが) 型レベルプログラミングがありたす。本圓に単玔な䟋だず、䞋蚘のようなものです。 type ExtendsObject < T > = T extends object ? true : false これが䜕をしおいるのかずいうず、型チェックの際に実斜される型蚈算を実装しおいるずいうこずです。䞊蚘のような条件分岐などのロゞックが型レベルで解決されおしたうずいうこずですね。 この型レベルプログラミングなのですが、衚珟力はさおおき、チュヌリング完党な系になっおしたっおいるずのもっぱらの評刀です。 型から型を新たに蚈算できおしたうずいうこずは  楜しいプログラミングの時間の始たりですね。 実装方針 長くなっおしたうので、以降では Brainf**k を BF ず蚘茉するこずにしたす。 BF 凊理系を型レベルに萜ずし蟌むにあたっお、次の 4 ぀の芁玠に分けお考えたす。 テヌプ構造䜓 ( Tape ) — メモリ・プログラムを共通しお衚珟するデヌタ構造 珟圚䜍眮を持ち぀぀、前埌に移動する胜力を持぀ 評䟡噚 ( Runner ) — メモリテヌプずプログラムテヌプを束ねた実行状態 メモリを倉化させ぀぀プログラムポむンタを移動するため、同時に扱う アクション ( Action ) — 1 ステップ実行の結果ずしお倖界に芁求する効果 (なにもしない / 入力 / 出力 / 終了) 評䟡ルヌプ ( Exec ) — アクションを解釈しお評䟡噚を回し、入力を消費し぀぀出力を蓄積するメむンルヌプ 型レベルプログラミングでは副䜜甚を玠盎に曞けないため、入出力を「アクション型」ずしおデヌタに萜ずしおおき、倖偎のルヌプでそれを解釈する圢にしたのがポむントです。以降、この順で各芁玠の実装を芋おいきたす。 たた、適宜参考実装ずしお Haskell の実装も合わせお瀺しおいたす。 TypeScript 実装は v5.4 以降で動䜜確認しおいたす。 実装䞊の制玄 制玄ずしお、実装レベルに効いおくるものもありたす。数倀での挔算や数倀⇔文字の倉換が基本的にできない、ずいうものです。䞍可胜ではないですが、タプル (型レベル配列) の length を取るような実装になりがちなのでたわりくどくなりたす。 今回は ASCII 範囲でむンクリメント・デクリメントを考えるだけなので、気合いで誀魔化すこずができたす。 NumToCharMap[65] のように参照するず 'A' ずいう型に解決される、ずいうマップを定矩したした。 数倀文字倉換、むンクリメント・デクリメントマップの実装 export type NumToCharMap = [ '\x00' , '\x01' , '\x02' , '\x03' , '\x04' , '\x05' , '\x06' , '\x07' , '\x08' , '\x09' , '\x0A' , '\x0B' , '\x0C' , '\x0D' , '\x0E' , '\x0F' , '\x10' , '\x11' , '\x12' , '\x13' , '\x14' , '\x15' , '\x16' , '\x17' , '\x18' , '\x19' , '\x1A' , '\x1B' , '\x1C' , '\x1D' , '\x1E' , '\x1F' , '\x20' , '\x21' , '\x22' , '\x23' , '\x24' , '\x25' , '\x26' , '\x27' , '\x28' , '\x29' , '\x2A' , '\x2B' , '\x2C' , '\x2D' , '\x2E' , '\x2F' , '\x30' , '\x31' , '\x32' , '\x33' , '\x34' , '\x35' , '\x36' , '\x37' , '\x38' , '\x39' , '\x3A' , '\x3B' , '\x3C' , '\x3D' , '\x3E' , '\x3F' , '\x40' , '\x41' , '\x42' , '\x43' , '\x44' , '\x45' , '\x46' , '\x47' , '\x48' , '\x49' , '\x4A' , '\x4B' , '\x4C' , '\x4D' , '\x4E' , '\x4F' , '\x50' , '\x51' , '\x52' , '\x53' , '\x54' , '\x55' , '\x56' , '\x57' , '\x58' , '\x59' , '\x5A' , '\x5B' , '\x5C' , '\x5D' , '\x5E' , '\x5F' , '\x60' , '\x61' , '\x62' , '\x63' , '\x64' , '\x65' , '\x66' , '\x67' , '\x68' , '\x69' , '\x6A' , '\x6B' , '\x6C' , '\x6D' , '\x6E' , '\x6F' , '\x70' , '\x71' , '\x72' , '\x73' , '\x74' , '\x75' , '\x76' , '\x77' , '\x78' , '\x79' , '\x7A' , '\x7B' , '\x7C' , '\x7D' , '\x7E' , '\x7F' , ] & { [ i: number ] : ' \x00 ' } ; export type CharToNumMap = { '\x00' : 0 x00 , '\x01' : 0 x01 , '\x02' : 0 x02 , '\x03' : 0 x03 , '\x04' : 0 x04 , '\x05' : 0 x05 , '\x06' : 0 x06 , '\x07' : 0 x07 , '\x08' : 0 x08 , '\x09' : 0 x09 , '\x0A' : 0 x0A , '\x0B' : 0 x0B , '\x0C' : 0 x0C , '\x0D' : 0 x0D , '\x0E' : 0 x0E , '\x0F' : 0 x0F , '\x10' : 0 x10 , '\x11' : 0 x11 , '\x12' : 0 x12 , '\x13' : 0 x13 , '\x14' : 0 x14 , '\x15' : 0 x15 , '\x16' : 0 x16 , '\x17' : 0 x17 , '\x18' : 0 x18 , '\x19' : 0 x19 , '\x1A' : 0 x1A , '\x1B' : 0 x1B , '\x1C' : 0 x1C , '\x1D' : 0 x1D , '\x1E' : 0 x1E , '\x1F' : 0 x1F , '\x20' : 0 x20 , '\x21' : 0 x21 , '\x22' : 0 x22 , '\x23' : 0 x23 , '\x24' : 0 x24 , '\x25' : 0 x25 , '\x26' : 0 x26 , '\x27' : 0 x27 , '\x28' : 0 x28 , '\x29' : 0 x29 , '\x2A' : 0 x2A , '\x2B' : 0 x2B , '\x2C' : 0 x2C , '\x2D' : 0 x2D , '\x2E' : 0 x2E , '\x2F' : 0 x2F , '\x30' : 0 x30 , '\x31' : 0 x31 , '\x32' : 0 x32 , '\x33' : 0 x33 , '\x34' : 0 x34 , '\x35' : 0 x35 , '\x36' : 0 x36 , '\x37' : 0 x37 , '\x38' : 0 x38 , '\x39' : 0 x39 , '\x3A' : 0 x3A , '\x3B' : 0 x3B , '\x3C' : 0 x3C , '\x3D' : 0 x3D , '\x3E' : 0 x3E , '\x3F' : 0 x3F , '\x40' : 0 x40 , '\x41' : 0 x41 , '\x42' : 0 x42 , '\x43' : 0 x43 , '\x44' : 0 x44 , '\x45' : 0 x45 , '\x46' : 0 x46 , '\x47' : 0 x47 , '\x48' : 0 x48 , '\x49' : 0 x49 , '\x4A' : 0 x4A , '\x4B' : 0 x4B , '\x4C' : 0 x4C , '\x4D' : 0 x4D , '\x4E' : 0 x4E , '\x4F' : 0 x4F , '\x50' : 0 x50 , '\x51' : 0 x51 , '\x52' : 0 x52 , '\x53' : 0 x53 , '\x54' : 0 x54 , '\x55' : 0 x55 , '\x56' : 0 x56 , '\x57' : 0 x57 , '\x58' : 0 x58 , '\x59' : 0 x59 , '\x5A' : 0 x5A , '\x5B' : 0 x5B , '\x5C' : 0 x5C , '\x5D' : 0 x5D , '\x5E' : 0 x5E , '\x5F' : 0 x5F , '\x60' : 0 x60 , '\x61' : 0 x61 , '\x62' : 0 x62 , '\x63' : 0 x63 , '\x64' : 0 x64 , '\x65' : 0 x65 , '\x66' : 0 x66 , '\x67' : 0 x67 , '\x68' : 0 x68 , '\x69' : 0 x69 , '\x6A' : 0 x6A , '\x6B' : 0 x6B , '\x6C' : 0 x6C , '\x6D' : 0 x6D , '\x6E' : 0 x6E , '\x6F' : 0 x6F , '\x70' : 0 x70 , '\x71' : 0 x71 , '\x72' : 0 x72 , '\x73' : 0 x73 , '\x74' : 0 x74 , '\x75' : 0 x75 , '\x76' : 0 x76 , '\x77' : 0 x77 , '\x78' : 0 x78 , '\x79' : 0 x79 , '\x7A' : 0 x7A , '\x7B' : 0 x7B , '\x7C' : 0 x7C , '\x7D' : 0 x7D , '\x7E' : 0 x7E , '\x7F' : 0 x7F , } & { [ k : string ]: 0 x00 ; } ; export type DecrementMap = [ 0 x7F , 0 x00 , 0 x01 , 0 x02 , 0 x03 , 0 x04 , 0 x05 , 0 x06 , 0 x07 , 0 x08 , 0 x09 , 0 x0A , 0 x0B , 0 x0C , 0 x0D , 0 x0E , 0 x0F , 0 x10 , 0 x11 , 0 x12 , 0 x13 , 0 x14 , 0 x15 , 0 x16 , 0 x17 , 0 x18 , 0 x19 , 0 x1A , 0 x1B , 0 x1C , 0 x1D , 0 x1E , 0 x1F , 0 x20 , 0 x21 , 0 x22 , 0 x23 , 0 x24 , 0 x25 , 0 x26 , 0 x27 , 0 x28 , 0 x29 , 0 x2A , 0 x2B , 0 x2C , 0 x2D , 0 x2E , 0 x2F , 0 x30 , 0 x31 , 0 x32 , 0 x33 , 0 x34 , 0 x35 , 0 x36 , 0 x37 , 0 x38 , 0 x39 , 0 x3A , 0 x3B , 0 x3C , 0 x3D , 0 x3E , 0 x3F , 0 x40 , 0 x41 , 0 x42 , 0 x43 , 0 x44 , 0 x45 , 0 x46 , 0 x47 , 0 x48 , 0 x49 , 0 x4A , 0 x4B , 0 x4C , 0 x4D , 0 x4E , 0 x4F , 0 x50 , 0 x51 , 0 x52 , 0 x53 , 0 x54 , 0 x55 , 0 x56 , 0 x57 , 0 x58 , 0 x59 , 0 x5A , 0 x5B , 0 x5C , 0 x5D , 0 x5E , 0 x5F , 0 x60 , 0 x61 , 0 x62 , 0 x63 , 0 x64 , 0 x65 , 0 x66 , 0 x67 , 0 x68 , 0 x69 , 0 x6A , 0 x6B , 0 x6C , 0 x6D , 0 x6E , 0 x6F , 0 x70 , 0 x71 , 0 x72 , 0 x73 , 0 x74 , 0 x75 , 0 x76 , 0 x77 , 0 x78 , 0 x79 , 0 x7A , 0 x7B , 0 x7C , 0 x7D , 0 x7E , ] & { [ i: number ] : 0x7F ; } ; export type IncrementMap = [ 0 x01 , 0 x02 , 0 x03 , 0 x04 , 0 x05 , 0 x06 , 0 x07 , 0 x08 , 0 x09 , 0 x0A , 0 x0B , 0 x0C , 0 x0D , 0 x0E , 0 x0F , 0 x10 , 0 x11 , 0 x12 , 0 x13 , 0 x14 , 0 x15 , 0 x16 , 0 x17 , 0 x18 , 0 x19 , 0 x1A , 0 x1B , 0 x1C , 0 x1D , 0 x1E , 0 x1F , 0 x20 , 0 x21 , 0 x22 , 0 x23 , 0 x24 , 0 x25 , 0 x26 , 0 x27 , 0 x28 , 0 x29 , 0 x2A , 0 x2B , 0 x2C , 0 x2D , 0 x2E , 0 x2F , 0 x30 , 0 x31 , 0 x32 , 0 x33 , 0 x34 , 0 x35 , 0 x36 , 0 x37 , 0 x38 , 0 x39 , 0 x3A , 0 x3B , 0 x3C , 0 x3D , 0 x3E , 0 x3F , 0 x40 , 0 x41 , 0 x42 , 0 x43 , 0 x44 , 0 x45 , 0 x46 , 0 x47 , 0 x48 , 0 x49 , 0 x4A , 0 x4B , 0 x4C , 0 x4D , 0 x4E , 0 x4F , 0 x50 , 0 x51 , 0 x52 , 0 x53 , 0 x54 , 0 x55 , 0 x56 , 0 x57 , 0 x58 , 0 x59 , 0 x5A , 0 x5B , 0 x5C , 0 x5D , 0 x5E , 0 x5F , 0 x60 , 0 x61 , 0 x62 , 0 x63 , 0 x64 , 0 x65 , 0 x66 , 0 x67 , 0 x68 , 0 x69 , 0 x6A , 0 x6B , 0 x6C , 0 x6D , 0 x6E , 0 x6F , 0 x70 , 0 x71 , 0 x72 , 0 x73 , 0 x74 , 0 x75 , 0 x76 , 0 x77 , 0 x78 , 0 x79 , 0 x7A , 0 x7B , 0 x7C , 0 x7D , 0 x7E , 0 x7F , 0 x00 , ] & { [ i: number ] : 0x00 ; } ; テヌプ構造䜓 BF では、メモリを甚意しおポむンタ操䜜・ポむンタを介した操䜜が前提になっおいたす。 もちろん型レベルプログラミングで副䜜甚は蚘述しにくいため、ポむンタ前提ずなっおいる郚分を再考し、同じ衚珟力の別の圢に眮き換える必芁がありたす。 メモリ、プログラムを同じテヌプ構造で捉えたす。今着目しおいる倀、その巊右に列が続いおいる様子を考えたのが䞋蚘のような構造になりたす。 テヌプ構造䜓の実装 このような構造䜓は、Haskell での data 宣蚀ず同じような圢で、TypeScript の型ではオブゞェクト型による宣蚀ができたす。 data Tape a = Tape { prevs :: [a] , curr :: a , nexts :: [a] } extends unknown[] によっお単なる配列型ではなく、各芁玠が独立した 型レベル配列ずしおのタプル を利甚できたす。 export type Tape < Hs extends unknown [], C , Ts extends unknown []> = { h : Hs c : C t : Ts } ここでいく぀かの基本的な操䜜も定矩しおしたいたしょう。 珟圚の倀に察する操䜜 むンクリメント・デクリメント 読み出し、曞き蟌み テヌプ䞊の移動 着目するヘッドを巊右に移動する操䜜 察応する [ , ] ぞのゞャンプは繰り返しによっお実珟する 基本操䜜の実装 Tape a から新しい Tape a を䜜る ( Tape a -> Tape a ) ずいう圢の実装ずなりたす。 -- | 次の芁玠に移動 next :: Tape a -> Tape a next (Tape prevs curr (n : nexts)) = Tape (curr : prevs) n nexts -- | 前の芁玠に移動 prev :: Tape a -> Tape a prev (Tape (p : prevs) curr nexts) = Tape prevs p (curr : nexts) -- | 珟圚の芁玠をむンクリメント incr :: Enum a => Tape a -> Tape a incr (Tape prevs curr nexts) = Tape prevs (succ curr) nexts -- | 珟圚の芁玠をデクリメント decr :: Enum a => Tape a -> Tape a decr (Tape prevs curr nexts) = Tape prevs (pred curr) nexts -- | 珟圚の芁玠を取埗 get :: Tape a -> a get (Tape _ curr _) = curr -- | 珟圚の芁玠を蚭定 put :: a -> Tape a -> Tape a put a (Tape prevs _ nexts) = Tape prevs a nexts TypeScript の型でも同様に、 Tape を受け取っお新しい Tape を䜜成するずいう方針で実装できたす。 [infer H, ...infer Hs] のパタヌンマッチングにより、型レベル配列の芁玠 (head, rest) を扱うこずができおしたいたす。 export type Prev < M > = M extends Tape < [infer H , ... infer Hs] , infer C , infer Ts > ? Tape< Hs , H , [C , ... Ts] > : never export type Next < M > = M extends Tape < infer Hs , infer C , [infer T , ... infer Ts] > ? Tape< [C , ... Hs] , T , Ts > : never export type Incr < M > = M extends Tape < infer Hs , infer C extends number , infer Ts > ? Tape< Hs , IncrementMap [C], Ts > : never export type Decr < M > = M extends Tape < infer Hs , infer C extends number , infer Ts > ? Tape< Hs , DecrementMap [C], Ts > : never export type PutC < M , C > = M extends Tape < infer Hs , unknown , infer Ts > ? Tape < Hs , C , Ts > : never プログラム実行 基本的な構造、操䜜は定矩しおしたったので、次はむンタプリタずしお重芁な実行に぀いお考えたす。 評䟡噚ずしおの実行系内郚 (メモリ・プログラムポむンタ) ず倖界ずのやりずりを含む効果の管理の郚分を、次のような圢で切り分けたす。 型レベルプログラミングでは入出力をそのたた扱うこずはできないので、入力埅ちや出力があるずいうこずは特別な状態ずしお衚珟するこずにしたす。 評䟡噚の内郚状態 こちらは至っおシンプルです。 状態はメモリ、プログラムのテヌプ (珟圚䜍眮を保持する) から成る これを評䟡に通すこずによっお、次の実行に関する状態が出おくる data Machine = Machine { memory :: DT.Tape Int , program :: DT.Tape Char } type Runner < M , P > = { mem : M prg : P } 倖郚ずのやりずりを含むアクション 今のメモリ・プログラムを含む、先皋の構造を評䟡しお埗られるアクションです。 -- | 䜕もしない、入力芁求、出力芁求、終了の 4 ぀のアクションを持぀ data WithAction a = ActionN { hold :: a } -- ^ 倖郚には䜕もしない | ActionI { hold :: a } -- ^ 入力芁求 | ActionO { hold :: a, out :: Int } -- ^ 出力芁求 | ActionE -- ^ 終了 これを型レベルプログラミングで再珟するず、ADT よりは個別の型ずしお定矩しおあげお、埌で extends などの条件分岐しおあげる方が玠盎になりたす。 type ActionN < R > = { action : "N" ; runner : R } type ActionI < R > = { action : "I" ; runner : R } type ActionO < R , O > = { action : "O" ; runner : R ; output : O } type ActionE = { action : "E" } 8 ぀の呜什に察する操䜜の敎理 評䟡噚の状態ずアクションを型ずしお定矩できたので、次はプログラムの瀺す呜什を凊理しおいく実装も考えおいきたす。 これは最初に確認した BF の 8 ぀の呜什に察しお、次の評䟡噚の状態ず蚈算の効果を含む党䜓を返す圢で定矩しおいけば良いです。 むンクリメント デクリメント 次を参照 (ポむンタむンクリメント) 前を参照 (ポむンタデクリメント) while (ゞャンプ) while end (ゞャンプバック) getchar putchar 呜什→次の状態・アクション さお、図で敎理できたので、実装にそのたた萜ずしおいきたす。 珟圚の呜什ポむンタが指す呜什に応じお、次の Action ず状態を返したす。 -- | 次のステップを実行し、状態ずアクションを返す step :: Machine -> WithAction Machine step machine = case pc of '+' -> ActionN $ machine { memory = DT.incr (memory machine), program = DT.next (program machine) } '-' -> ActionN $ machine { memory = DT.decr (memory machine), program = DT.next (program machine) } '>' -> ActionN $ machine { memory = DT.next (memory machine), program = DT.next (program machine) } '<' -> ActionN $ machine { memory = DT.prev (memory machine), program = DT.next (program machine) } '[' -> ActionN $ machine { program = if mc == 0 then skip (program machine) else DT.next (program machine) } ']' -> ActionN $ machine { program = if mc /= 0 then back (program machine) else DT.next (program machine) } ',' -> ActionI { hold = machine { program = DT.next (program machine) } } '.' -> ActionO { hold = machine { program = DT.next (program machine) }, out = DT.get (memory machine) } _ -> ActionE where (pc, mc) = (,) <$> DT.get . program <*> DT.get . memory $ machine TypeScript で曞いおも、ほずんど同じ察応がありたす。 type Step < R > = R extends Runner < infer M extends TapeMm , infer P extends TapePg > ? P[ 'c' ] extends '+' ? ActionN< Runner < Incr < M >, Next < P >>> : P[ 'c' ] extends '-' ? ActionN< Runner < Decr < M >, Next < P >>> : P[ 'c' ] extends '>' ? ActionN< Runner < Next < M >, Next < P >>> : P[ 'c' ] extends '<' ? ActionN< Runner < Prev < M >, Next < P >>> : P[ 'c' ] extends '[' ? ActionN< Runner < M , M [ 'c' ] extends 0 ? Skip < P > : Next < P >>> : P[ 'c' ] extends ']' ? ActionN< Runner < M , M [ 'c' ] extends 0 ? Next < P > : Back < P >>> : P[ 'c' ] extends ',' ? ActionI< Runner < M , Next < P >>> : P[ 'c' ] extends '.' ? ActionO< Runner < M , Next < P >>, M [ 'c' ]> : ActionE : never; 状態・アクション→継続 次は状態、アクションを受けお、次のステップに継続しおいくルヌプを実装しおいきたす。 䞊蚘の step を実行し、その Action に応じた操䜜を実行しおいきたす。 -- | 入力を消費・出力を収集しながら step を繰り返す loop :: (Machine -> WithAction Machine) -> (String, Machine) -> String loop step (input, machine) = go (step machine) where -- アクションに察応した動䜜を実行し、再垰に進む go (ActionN machine') = loop step (input, machine') -- そのたた次ぞ go (ActionI machine') = loop step (iTail, machine'') where -- 入力を消費しおメモリに曞き蟌み、次に進む (iHead : iTail) = input machine'' = machine' { memory = DT.put (fromEnum iHead) (memory machine') } go (ActionO machine' out) = toEnum out : loop step (input, machine') -- 出力を収集し、次に進む go ActionE = [] -- 終端 TypeScript の型の方では、今回は文字列の环積を保持する圢で実装しおいたす。ちょっず呜名が異なっおしたっおいたすが、やっおいるこずは同じです。 type Exec < R , I extends string , O extends string = '' > = Step < R > extends infer WithAction ? WithAction extends ActionN< infer Q > ? Exec< Q , I , O > : WithAction extends ActionI< infer Q > ? I extends ` ${ infer F }${ infer S } ` ? Exec< Read < Q , CharToNumMap [F]>, S , O > : Exec< Read < Q , 0>, I , O > : WithAction extends ActionO< infer Q , infer N extends number > ? Exec< Q , I , ` ${ O }${ NumToCharMap [N] } ` > : WithAction extends ActionE ? O : never : never; たずめ TypeScript で Brainf**k 凊理系の型レベルプログラムの実装に぀いお芋おいきたした。 補足ずしお、TypeScript の型レベルプログラミング実行系には次のような制玄がありたす。 型の再垰評䟡回数、぀たり実行できるステップ数が制限されおいる Tape 構造䜓の保持する芁玠列の長さに制限がある (どちらも倧䜓 1,000 皋床のむメヌゞ) 䞀方、このような制限がある䞭でも、冒頭に瀺した䟋のように簡単な Hello World の䟋たでは実装できおしたいたす。 みなさんもぜひ自分の型レベルプログラミングに挑戊しおみおください。 私が今回瀺した実装も最善ではないず思いたす。「もっず良いものを曞いおみよう」など、楜しんでみおください。 付録 Haskell のコヌド党文を掲茉しおおきたす。 cwd-k2/bf-in-type のリポゞトリ ず比范する、たたは手元でテスト実行するなどしおください。 ディレクトリ構成 . ├── Data │   └── Tape.hs ├── Interpreter.hs └── Main.hs Data/Tape.hs module Data.Tape ( Tape( .. ), zeros, fromList, next, prev, incr, decr, get, put, ) where -- | テヌプ様構造䜓 -- * 前埌に無限に芁玠があり、珟圚芁玠 (針の先にあるもの) を䞭心に配眮しおいる -- -- > <-prev- ... 4 5 6 <<7>> 8 9 10 ... -next-> data Tape a = Tape { prevs :: [a] , curr :: a , nexts :: [a] } deriving Show -- | れロ初期化された無限長のテヌプ zeros :: Enum a => Tape a zeros = Tape (repeat $ toEnum 0 ) (toEnum 0 ) (repeat $ toEnum 0 ) -- | リストからテヌプを䜜成 fromList :: [a] -> Tape a fromList (x : xs) = Tape [] x xs fromList [] = undefined -- 今回は特に考えずに未定矩ずする -- | 次の芁玠に移動 next :: Tape a -> Tape a next (Tape prevs curr (n : nexts)) = Tape (curr : prevs) n nexts -- | 前の芁玠に移動 prev :: Tape a -> Tape a prev (Tape (p : prevs) curr nexts) = Tape prevs p (curr : nexts) -- | 珟圚の芁玠をむンクリメント incr :: Enum a => Tape a -> Tape a incr (Tape prevs curr nexts) = Tape prevs (succ curr) nexts -- | 珟圚の芁玠をデクリメント decr :: Enum a => Tape a -> Tape a decr (Tape prevs curr nexts) = Tape prevs (pred curr) nexts -- | 珟圚の芁玠を取埗 get :: Tape a -> a get (Tape _ curr _) = curr -- | 珟圚の芁玠を蚭定 put :: a -> Tape a -> Tape a put a (Tape prevs _ nexts) = Tape prevs a nexts Interpreter.hs module Interpreter ( bf ) where import qualified Data.Tape as DT import Data.List (unfoldr) -- | メモリずプログラムを持぀ data Machine = Machine { memory :: DT.Tape Int , program :: DT.Tape Char } deriving Show -- | 䜕もしない、入力芁求、出力芁求、終了の 4 ぀のアクションを持぀ data WithAction a = ActionN { hold :: a } -- ^ 倖郚には䜕もしない | ActionI { hold :: a } -- ^ 入力芁求 | ActionO { hold :: a, out :: Int } -- ^ 出力芁求 | ActionE -- ^ 終了 deriving Show -- | 察応する @']'@ たでプログラムをスキップする skip :: DT.Tape Char -> DT.Tape Char skip = skipInner 0 where skipInner n program = let program' = DT.next program in case DT.get program' of '[' -> skipInner (n + 1 ) program' ']' -> if n == 0 then program' else skipInner (n - 1 ) program' _ -> skipInner n program' -- | 察応する @'['@ たでプログラムを戻す back :: DT.Tape Char -> DT.Tape Char back = backInner 0 where backInner n program = let program' = DT.prev program in case DT.get program' of ']' -> backInner (n + 1 ) program' '[' -> if n == 0 then program' else backInner (n - 1 ) program' _ -> backInner n program' -- | 次のステップを実行し、状態ずアクションを返す step :: Machine -> WithAction Machine step machine = case pc of '+' -> ActionN $ machine { memory = DT.incr (memory machine), program = DT.next (program machine) } '-' -> ActionN $ machine { memory = DT.decr (memory machine), program = DT.next (program machine) } '>' -> ActionN $ machine { memory = DT.next (memory machine), program = DT.next (program machine) } '<' -> ActionN $ machine { memory = DT.prev (memory machine), program = DT.next (program machine) } '[' -> ActionN $ machine { program = if mc == 0 then skip (program machine) else DT.next (program machine) } ']' -> ActionN $ machine { program = if mc /= 0 then back (program machine) else DT.next (program machine) } ',' -> ActionI { hold = machine { program = DT.next (program machine) } } '.' -> ActionO { hold = machine { program = DT.next (program machine) }, out = DT.get (memory machine) } _ -> ActionE where (pc, mc) = (,) <$> DT.get . program <*> DT.get . memory $ machine -- | 入力を消費・出力を収集しながら step を繰り返す loop :: (Machine -> WithAction Machine) -> (String, Machine) -> String loop step (input, machine) = go (step machine) where -- アクションに察応した動䜜を実行し、再垰に進む go (ActionN machine') = loop step (input, machine') -- そのたた次ぞ go (ActionI machine') = loop step (iTail, machine'') where -- 入力を消費しおメモリに曞き蟌み、次に進む (iHead : iTail) = input machine'' = machine' { memory = DT.put (fromEnum iHead) (memory machine') } go (ActionO machine' out) = toEnum out : loop step (input, machine') -- 出力を収集し、次に進む go ActionE = [] -- 終端 -- | Bf プログラムから、入力を受け取っお出力を返す関数を䜜る bf :: String -> [Char] -> String bf program input = loop step (input', machine) where input' = input ++ repeat ' \0 ' machine = Machine { memory = DT.zeros , program = DT.next $ DT.fromList ( "#" ++ program ++ "#" ) } Main.hs module Main where import Interpreter -- | ハロヌワヌルドする Bf プログラム helloWorld :: String helloWorld = "++++++++++[>+++++++>++++++++++>+++++++++++>+++>+++++++++>+<<<<<<-]>++.>+.>--..+++.>++.>---.<<.+++.------.<-.>>+.>>." -- | ゚コヌする Bf プログラム echo :: String echo = "+[,.]" main :: IO () main = do let getOutputBf = bf helloWorld putStr $ getOutputBf "こんにちは \n "

動画

該圓するコンテンツが芋぀かりたせんでした

曞籍