2019への参加レポートとして、私の印象に残った発表を紹介します。今回は、Copilotという、C言語のコードを生成するHaskell製内部DSLについての発表です。MethodsHaskell製の内部DSLからC言語のソースコードを生成する、Copilotの紹介です。似た謳い文句の内部DSLとしてivoryがありますが、Copilotは、ハードウェアの実行時検証を行うC言語のコードを生成することに、より特化しています。「センサーから信号を受け取って、一定の条件を満たした場合に何らかの処理を実行する」という処理をHaskellで宣言的に記述すると、メモリの消費量・実行時間において常に一定なC言語のコードを生成することが出来ます。メモリが限られていて、リアルタイムな処理が必要なハードウェアにとって「邪魔にならない監視」を実現するための必須条件なのでしょう。現状HaskellはGCが必要であるといった制約もあり、リアルタイムな処理や厳格なメモリー管理が必要な機器での採用は難しいですが、Ivoryや今回発表されたCopilotはあくまでも「C言語のコードを生成するだけ」なので、生成するHaskellではメモリー管理をする必要がありません。にっくきスペースリークに悩まされる心配もないのです。いったHaskell製内部DSLは、Haskellの持つ強い型付けによるメリットを享受しながら、変換した言語の実行時におけるパフォーマンスを出しやすい、といういいとこ取りなメリットがあるので、もっと広まってほしいユースケースですね。BlogのGitHubのリポジトリーにあります。使用したcopilotパッケージのバージョンは、3.0.1です。サンプルコードの解説については、notogawaさんのアドバイスも参考になりました(Haskell-jpのslack-logではこのあたり。執筆時点でCSSが当たってないため読みづらいですが一応)。ありがとうございます!せっかくなんでCopilotを試してみましょう。公式サイトにあったサンプルコードそのまんまですが、生成されるCのコードを眺めてみます。blog/examples/2019/hiw-copilot👇のコマンドでビルド・C言語によるコードの生成できるはずです。heater.hsこちらが生成元のHaskellのコードです。"heater"まず、tempとctempという識別子に定義した式が、センサーが発信する、連続的に変化する値を表しています。Copilotの言葉はこれをStreamと呼んでいます。specという識別子で定義している式が、「どのセンサーから信号を受け取って、どんな条件を満たした場合にどの処理を実行するか」規定しているようです。👆の場合、ctempというStreamが18.0を下回ったらheatonというイベントを発火し、21.0を超えたらheatoffというイベントを発火する、と定めているわけですね。"heater"という箇所で、.hファイルと.cファイルを書き込んでいます。step(void);で、肝心のCのコード本体heater.cがこちらです。};}先ほどStreamとして定義した値のうち、tempは、temperatureというグローバル変数と、それを一時的に保存するtemperature_cpyという二つの変数に翻訳されました。specにおいてtriggerという関数で列挙した「どのセンサーから信号を受け取って、どんな条件を満たした場合にどの処理を実行するか」というルールは、stepという関数に現れたようです。この関数を利用する側では、heaton関数とheatoff関数を別途定義した上で、temperatureにセンサーから受け取った値を代入してstepを呼ぶことによって、temperatureの値が条件に一致したとき、heaton関数とheatoff関数を実行してハードウェアの制御ができるのでしょう。Haskell側で定義したもう一つのStream、ctempは、heaton_guard、heaton_arg0、heatoff_guard、heatoff_arg0、それぞれの関数に書かれた、temperature_cpyの値を変換する式に現れているようです。正直なところこの程度であれば、直接Cで書いた方が余計なカッコもないし読みやすそうではあります。50.0が変換後のソースコードでは冗長に適用されていることから、もっと最適化できそうですし。いえ、わざわざDSLを作ったからには、より複雑で、Haskellでなければ書いてられないようなケースが、Copilotの開発者の現場ではあるのでしょう(なんせNASAの方も関わっているぐらいですから!)。詳しいユースケースや、ビルド時のフローといった運用方法を聞きたいところですね。ライセンスの下に提供されています。Analyticsでアクセス情報を収集しています。集めた情報は統計的に処理した上で、当ウェブサイトの改善のための参考情報としてのみ使用します。