Skip to content

Commit f03aa1a

Browse files
authored
Merge pull request #172 from haskell-jp/hiw2019-copilot
記事の追加: HIW 2019で発表された、Copilotという内部DSLについて
2 parents f510258 + 0a9f4e8 commit f03aa1a

File tree

6 files changed

+366
-0
lines changed

6 files changed

+366
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.stack-work/
2+
*~
3+
*.exe
4+
*.o
5+
*.hi
6+
*.c
7+
*.h
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# How to build
2+
3+
```
4+
stack build copilot
5+
stack exec ghc heater.hs
6+
```
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{-
2+
This example is copied from https://copilot-language.github.io/
3+
4+
(c) 2009 Frank Dedden, Nis Nordby Wegmann, Lee Pike, Robin Morisset, Sebastian Niller, Alwyn Goodloe
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions
8+
are met:
9+
10+
Redistributions of source code must retain the above copyright
11+
notice, this list of conditions and the following disclaimer.
12+
13+
Redistributions in binary form must reproduce the above copyright
14+
notice, this list of conditions and the following disclaimer in the
15+
documentation and/or other materials provided with the distribution.
16+
17+
Neither the name of the developers nor the names of its contributors
18+
may be used to endorse or promote products derived from this software
19+
without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32+
-}
33+
34+
-- This is a simple example with basic usage. It implements a simple home
35+
-- heating system: It heats when temp gets too low, and stops when it is high
36+
-- enough. It read temperature as a byte (range -50C to 100C) and translates
37+
-- this to Celcius.
38+
39+
import Language.Copilot
40+
import Copilot.Compile.C99
41+
42+
import Prelude hiding ((>), (<), div)
43+
44+
-- External temperature as a byte, range of -50C to 100C
45+
temp :: Stream Word8
46+
temp = extern "temperature" Nothing
47+
48+
-- Calculate temperature in Celcius.
49+
-- We need to cast the Word8 to a Float. Note that it is an unsafeCast, as there
50+
-- is no direct relation between Word8 and Float.
51+
ctemp :: Stream Float
52+
ctemp = (unsafeCast temp) * (150.0 / 255.0) - 50.0
53+
54+
spec = do
55+
-- Triggers that fire when the ctemp is too low or too high,
56+
-- pass the current ctemp as an argument.
57+
trigger "heaton" (ctemp < 18.0) [arg ctemp]
58+
trigger "heatoff" (ctemp > 21.0) [arg ctemp]
59+
60+
-- Compile the spec
61+
main = reify spec >>= compile "heater"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# This file was automatically generated by 'stack init'
2+
#
3+
# Some commonly used options have been documented as comments in this file.
4+
# For advanced use and comprehensive documentation of the format, please see:
5+
# https://docs.haskellstack.org/en/stable/yaml_configuration/
6+
7+
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
8+
# A snapshot resolver dictates the compiler version and the set of packages
9+
# to be used for project dependencies. For example:
10+
#
11+
# resolver: lts-3.5
12+
# resolver: nightly-2015-09-21
13+
# resolver: ghc-7.10.2
14+
#
15+
# The location of a snapshot can be provided as a file or url. Stack assumes
16+
# a snapshot provided as a file might change, whereas a url resource does not.
17+
#
18+
# resolver: ./custom-snapshot.yaml
19+
# resolver: https://example.com/snapshots/2018-01-01.yaml
20+
resolver: lts-14.6
21+
22+
# User packages to be built.
23+
# Various formats can be used as shown in the example below.
24+
#
25+
# packages:
26+
# - some-directory
27+
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
28+
# subdirs:
29+
# - auto-update
30+
# - wai
31+
packages: []
32+
# Dependency packages to be pulled from upstream that are not in the resolver.
33+
# These entries can reference officially published versions as well as
34+
# forks / in-progress versions pinned to a git hash. For example:
35+
#
36+
# extra-deps:
37+
# - acme-missiles-0.3
38+
# - git: https://github.com/commercialhaskell/stack.git
39+
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
40+
#
41+
extra-deps:
42+
- copilot-3.0.1
43+
- copilot-c99-3.0.2@sha256:e84e435eeb7eb0e8e69a016aae5a18c07980cb7caa4d049c29d21d96bf55df91,2274
44+
- copilot-core-3.0.1@sha256:cd5f4d47fba9e717b274cf393a86c8d60103c009c694f570746401f085b14426,2121
45+
- copilot-language-3.0.1@sha256:581c6cc1205b8e5022e1afee54149de0c84a4c31f9e8412aab947d1a979075db,2937
46+
- copilot-libraries-3.0@sha256:e085b7f1ff3f8a0cb307c03ffffbe930e1eeabdf962d07431b8b74f6e8d434de,1966
47+
- copilot-theorem-3.0@sha256:a04e72748753e279f17f814c953100490ddfd65c7860b29b72b0c18f8f3955ef,4225
48+
- bimap-0.3.3@sha256:232518c0410990665b9c8677eb9318ee355c001d58945ddcbedec3baa30b4160,1475
49+
- language-c99-0.1.1@sha256:c06430bfa5b52cefc8cce142ea9a1e52b2900f5d84408d6b198ddcdb1c5bff0a,1587
50+
- language-c99-simple-0.1.2@sha256:5b522616fa22a0909250d56a0632f1f1cae1c38d585149e13eeb809103af9543,1540
51+
- language-c99-util-0.1.1@sha256:480adee2ba3045d04305be5362be991b1814faf58cfd7af1f845d0fc9dbcf3a7,1177
52+
53+
# Override default flag values for local packages and extra-deps
54+
# flags: {}
55+
56+
# Extra package databases containing global packages
57+
# extra-package-dbs: []
58+
59+
# Control whether we use the GHC we find on the path
60+
# system-ghc: true
61+
#
62+
# Require a specific version of stack, using version ranges
63+
# require-stack-version: -any # Default
64+
# require-stack-version: ">=2.1"
65+
#
66+
# Override the architecture used by stack, especially useful on Windows
67+
# arch: i386
68+
# arch: x86_64
69+
#
70+
# Extra directories used by stack for building
71+
# extra-include-dirs: [/path/to/dir]
72+
# extra-lib-dirs: [/path/to/dir]
73+
#
74+
# Allow a newer minor version of GHC than the snapshot specifies
75+
# compiler-check: newer-minor
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# This file was autogenerated by Stack.
2+
# You should not edit this file by hand.
3+
# For more information, please see the documentation at:
4+
# https://docs.haskellstack.org/en/stable/lock_files
5+
6+
packages:
7+
- completed:
8+
hackage: copilot-3.0.1@sha256:7faa685324a3a3b0f1b2ba1638ba2c99a9c2887105af9f0b9f14b3236c22a44f,2040
9+
pantry-tree:
10+
size: 568
11+
sha256: 80ee00b549a78aec160b62a30e543a3366b80fa6e6501a8c2487f99b993daa62
12+
original:
13+
hackage: copilot-3.0.1
14+
- completed:
15+
hackage: copilot-c99-3.0.2@sha256:e84e435eeb7eb0e8e69a016aae5a18c07980cb7caa4d049c29d21d96bf55df91,2274
16+
pantry-tree:
17+
size: 644
18+
sha256: 71f79a08fadf3aae2bab3a807c7ad63167319014bc8c5bf586cde49fe5ca4344
19+
original:
20+
hackage: copilot-c99-3.0.2@sha256:e84e435eeb7eb0e8e69a016aae5a18c07980cb7caa4d049c29d21d96bf55df91,2274
21+
- completed:
22+
hackage: copilot-core-3.0.1@sha256:cd5f4d47fba9e717b274cf393a86c8d60103c009c694f570746401f085b14426,2121
23+
pantry-tree:
24+
size: 1882
25+
sha256: f68e6f0f28a7ae705799cf37feadf237fa3d3c258fb0e53b7792a665e9e328b0
26+
original:
27+
hackage: copilot-core-3.0.1@sha256:cd5f4d47fba9e717b274cf393a86c8d60103c009c694f570746401f085b14426,2121
28+
- completed:
29+
hackage: copilot-language-3.0.1@sha256:581c6cc1205b8e5022e1afee54149de0c84a4c31f9e8412aab947d1a979075db,2937
30+
pantry-tree:
31+
size: 2181
32+
sha256: 3bb29b06dc89d2e41da34e2945044e080938ef19c21740e8846dca02ff7d9ca4
33+
original:
34+
hackage: copilot-language-3.0.1@sha256:581c6cc1205b8e5022e1afee54149de0c84a4c31f9e8412aab947d1a979075db,2937
35+
- completed:
36+
hackage: copilot-libraries-3.0@sha256:e085b7f1ff3f8a0cb307c03ffffbe930e1eeabdf962d07431b8b74f6e8d434de,1966
37+
pantry-tree:
38+
size: 909
39+
sha256: f7cbae21502cccbdc7214b86095d52b78eb479f8c16902cfa987dff0b6dcde16
40+
original:
41+
hackage: copilot-libraries-3.0@sha256:e085b7f1ff3f8a0cb307c03ffffbe930e1eeabdf962d07431b8b74f6e8d434de,1966
42+
- completed:
43+
hackage: copilot-theorem-3.0@sha256:a04e72748753e279f17f814c953100490ddfd65c7860b29b72b0c18f8f3955ef,4225
44+
pantry-tree:
45+
size: 2624
46+
sha256: 630999d9ee57e73a0e6babcd0cf3df93ffda748220ab0d7f81f7fa4ef27ecb20
47+
original:
48+
hackage: copilot-theorem-3.0@sha256:a04e72748753e279f17f814c953100490ddfd65c7860b29b72b0c18f8f3955ef,4225
49+
- completed:
50+
hackage: bimap-0.3.3@sha256:232518c0410990665b9c8677eb9318ee355c001d58945ddcbedec3baa30b4160,1475
51+
pantry-tree:
52+
size: 414
53+
sha256: 5c7485014c2f342d215ace99cd06361f7607169aebe8d4aca2f265fa5c3c6761
54+
original:
55+
hackage: bimap-0.3.3@sha256:232518c0410990665b9c8677eb9318ee355c001d58945ddcbedec3baa30b4160,1475
56+
- completed:
57+
hackage: language-c99-0.1.1@sha256:c06430bfa5b52cefc8cce142ea9a1e52b2900f5d84408d6b198ddcdb1c5bff0a,1587
58+
pantry-tree:
59+
size: 399
60+
sha256: de3e5af62744508f8271c053ad2c598b6819e6fafeece4e3b5d27cf71e0d3a59
61+
original:
62+
hackage: language-c99-0.1.1@sha256:c06430bfa5b52cefc8cce142ea9a1e52b2900f5d84408d6b198ddcdb1c5bff0a,1587
63+
- completed:
64+
hackage: language-c99-simple-0.1.2@sha256:5b522616fa22a0909250d56a0632f1f1cae1c38d585149e13eeb809103af9543,1540
65+
pantry-tree:
66+
size: 574
67+
sha256: 1d297b46c78e2a498f7828bddc794d8355656273f7334f8ab27452ccfd28d5be
68+
original:
69+
hackage: language-c99-simple-0.1.2@sha256:5b522616fa22a0909250d56a0632f1f1cae1c38d585149e13eeb809103af9543,1540
70+
- completed:
71+
hackage: language-c99-util-0.1.1@sha256:480adee2ba3045d04305be5362be991b1814faf58cfd7af1f845d0fc9dbcf3a7,1177
72+
pantry-tree:
73+
size: 489
74+
sha256: 3a5e1387e9b073dad4f51fa749fd4c33e2abe31566b63a120a38a7f88d809493
75+
original:
76+
hackage: language-c99-util-0.1.1@sha256:480adee2ba3045d04305be5362be991b1814faf58cfd7af1f845d0fc9dbcf3a7,1177
77+
snapshots:
78+
- completed:
79+
size: 524127
80+
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/6.yaml
81+
sha256: dc70dfb45e2c32f54719819bd055f46855dd4b3bd2e58b9f3f38729a2d553fbb
82+
original: lts-14.6
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
---
2+
title: HIW 2019で発表された、Copilotという内部DSLについて
3+
subHeading: ~HIW 2019参加レポート その4~
4+
headingBackgroundImage: ../../img/background.png
5+
headingDivClass: post-heading
6+
author: Yuji Yamamoto
7+
postedBy: <a href="http://the.igreque.info/">Yuji Yamamoto(@igrep)</a>
8+
date: October 1, 2019
9+
tags: Haskell Implementors' Workshop
10+
...
11+
---
12+
13+
[前回](/posts/2019/hiw-gibbon.html)から引き続き、[Haskell Implementors' Workshop 2019](https://icfp19.sigplan.org/home/hiw-2019#About)への参加レポートとして、私の印象に残った発表を紹介します。
14+
今回は、[Copilot](https://copilot-language.github.io/)という、C言語のコードを生成するHaskell製内部DSLについての発表です。
15+
16+
# Copilot 3.0: a Haskell runtime verification framework for UAVs
17+
18+
発表者: Frank Dedden *Royal Netherlands Aerospace Center*, Alwyn Goodloe *NASA Langley Research Center*, Ivan Perez *NIA / NASA Formal Methods*
19+
20+
Haskell製の内部DSLからC言語のソースコードを生成する、[Copilot](https://copilot-language.github.io/)の紹介です。
21+
似た謳い文句の内部DSLとして[ivory](http://hackage.haskell.org/package/ivory)がありますが、Copilotは、ハードウェアの実行時検証を行うC言語のコードを生成することに、より特化しています。
22+
「センサーから信号を受け取って、一定の条件を満たした場合に何らかの処理を実行する」という処理をHaskellで宣言的に記述すると、メモリの消費量・実行時間において常に一定なC言語のコードを生成することが出来ます。
23+
24+
メモリが限られていて、リアルタイムな処理が必要なハードウェアにとって「邪魔にならない監視」を実現するための必須条件なのでしょう。
25+
現状HaskellはGCが必要であるといった制約もあり、リアルタイムな処理や厳格なメモリー管理が必要な機器での採用は難しいですが、Ivoryや今回発表されたCopilotはあくまでも「C言語のコードを生成するだけ」なので、生成するHaskellではメモリー管理をする必要がありません。
26+
にっくきスペースリークに悩まされる心配もないのです。
27+
こういったHaskell製内部DSLは、Haskellの持つ強い型付けによるメリットを享受しながら、変換した言語の実行時におけるパフォーマンスを出しやすい、といういいとこ取りなメリットがあるので、もっと広まってほしいユースケースですね。
28+
29+
# Copilotを試してみる
30+
31+
- ℹ️ 実際に使用したコードは[Haskell-jp BlogのGitHubのリポジトリー](https://github.com/haskell-jp/blog/tree/master/examples/2019/hiw-copilot)にあります。
32+
- ℹ️ 使用したcopilotパッケージのバージョンは、3.0.1です。
33+
- ℹ️ サンプルコードの解説については、notogawaさんのアドバイスも参考になりました<small>([Haskell-jpのslack-logではこのあたり](https://haskell.jp/slack-log/html/C4M4TT8JJ/46.html#message-1554858057.072700)。執筆時点でCSSが当たってないため読みづらいですが一応)</small>。ありがとうございます!
34+
35+
せっかくなんでCopilotを試してみましょう。
36+
公式サイトにあったサンプルコードそのまんまですが、生成されるCのコードを眺めてみます。
37+
38+
👇のコマンドでサンプルコードが入ったリポジトリーをgit cloneした後、
39+
40+
```bash
41+
git clone https://github.com/haskell-jp/blog
42+
cd blog/examples/2019/hiw-copilot
43+
```
44+
45+
👇のコマンドでビルド・C言語によるコードの生成できるはずです。
46+
47+
```bash
48+
stack build copilot
49+
stack exec runghc heater.hs
50+
```
51+
52+
こちらが生成元のHaskellのコードです。
53+
54+
```haskell:heater.hs
55+
import Language.Copilot
56+
import Copilot.Compile.C99
57+
58+
import Prelude hiding ((>), (<), div)
59+
60+
temp :: Stream Word8
61+
temp = extern "temperature" Nothing
62+
63+
ctemp :: Stream Float
64+
ctemp = (unsafeCast temp) * (150.0 / 255.0) - 50.0
65+
66+
spec = do
67+
trigger "heaton" (ctemp < 18.0) [arg ctemp]
68+
trigger "heatoff" (ctemp > 21.0) [arg ctemp]
69+
70+
main = reify spec >>= compile "heater"
71+
```
72+
73+
まず、`temp``ctemp`という識別子に定義した式が、センサーが発信する、連続的に変化する値を表しています。
74+
Copilotの言葉はこれを`Stream`と呼んでいます。
75+
76+
`spec`という識別子で定義している式が、「どのセンサーから信号を受け取って、どんな条件を満たした場合にどの処理を実行するか」規定しているようです。
77+
👆の場合、`ctemp`という`Stream``18.0`を下回ったら`heaton`というイベントを発火し、`21.0`を超えたら`heatoff`というイベントを発火する、と定めているわけですね。
78+
そして`main`関数で実行している`reify spec >>= compile "heater"`という箇所で、`.h`ファイルと`.c`ファイルを書き込んでいます。
79+
80+
そして、生成されたヘッダーファイル`heater.h`がこう👇
81+
82+
```c:heater.h
83+
extern uint8_t temperature;
84+
void heatoff(float heatoff_arg0);
85+
void heaton(float heaton_arg0);
86+
void step(void);
87+
```
88+
89+
で、肝心のCのコード本体`heater.c`がこちらです。
90+
91+
```c:heater.c
92+
#include <stdint.h>
93+
#include <stdbool.h>
94+
#include <string.h>
95+
96+
#include "heater.h"
97+
98+
static uint8_t temperature_cpy;
99+
100+
bool heatoff_guard(void) {
101+
return ((((float)(temperature_cpy)) * ((150.0) / (255.0))) - (50.0)) > (21.0);
102+
}
103+
104+
float heatoff_arg0(void) {
105+
return (((float)(temperature_cpy)) * ((150.0) / (255.0))) - (50.0);
106+
}
107+
108+
bool heaton_guard(void) {
109+
return ((((float)(temperature_cpy)) * ((150.0) / (255.0))) - (50.0)) < (18.0);
110+
}
111+
112+
float heaton_arg0(void) {
113+
return (((float)(temperature_cpy)) * ((150.0) / (255.0))) - (50.0);
114+
}
115+
116+
void step(void) {
117+
(temperature_cpy) = (temperature);
118+
if ((heatoff_guard)()) {
119+
(heatoff)(((heatoff_arg0)()));
120+
};
121+
if ((heaton_guard)()) {
122+
(heaton)(((heaton_arg0)()));
123+
};
124+
}
125+
```
126+
127+
先ほど`Stream`として定義した値のうち、`temp`は、`temperature`というグローバル変数と、それを一時的に保存する`temperature_cpy`という二つの変数に翻訳されました。
128+
`spec`において`trigger`という関数で列挙した「どのセンサーから信号を受け取って、どんな条件を満たした場合にどの処理を実行するか」というルールは、`step`という関数に現れたようです。
129+
この関数を利用する側では、`heaton`関数と`heatoff`関数を別途定義した上で、`temperature`にセンサーから受け取った値を代入して`step`を呼ぶことによって、`temperature`の値が条件に一致したとき、`heaton`関数と`heatoff`関数を実行してハードウェアの制御ができるのでしょう。
130+
Haskell側で定義したもう一つの`Stream``ctemp`は、`heaton_guard``heaton_arg0``heatoff_guard``heatoff_arg0`、それぞれの関数に書かれた、`temperature_cpy`の値を変換する式に現れているようです。
131+
132+
正直なところこの程度であれば、直接Cで書いた方が余計なカッコもないし読みやすそうではあります。
133+
`temp``ctemp`に変換する式`(150.0 / 255.0) - 50.0`が変換後のソースコードでは冗長に適用されていることから、もっと最適化できそうですし。
134+
とはいえ、わざわざDSLを作ったからには、より複雑で、Haskellでなければ書いてられないようなケースが、Copilotの開発者の現場ではあるのでしょう<small>(なんせNASAの方も関わっているぐらいですから!)</small>。
135+
詳しいユースケースや、ビルド時のフローといった運用方法を聞きたいところですね。

0 commit comments

Comments
 (0)