SI屋のためのF# ~DSL編~

27
屋のための 編~

description

函数型なんたらの集い 2014 in Tokyoでの発表資料です。

Transcript of SI屋のためのF# ~DSL編~

Page 1: SI屋のためのF# ~DSL編~

SI屋のためのF#

~DSL編~

bleis-tift

October 25, 2014

Page 2: SI屋のためのF# ~DSL編~

自己紹介

id:bleis-tift / @bleis

なごやではたらくゆるふわ F#er

仕事で F#使ってます!

Page 3: SI屋のためのF# ~DSL編~

今日話すこと

ざっくり:SI屋がF#をどのように業務に使っているかの一例

もうちょい詳しく:内部DSLとか外部DSLを F#で作って 1ソースである特定のコードと仕様書を出力してハッピー!

Page 4: SI屋のためのF# ~DSL編~

背景

弊社について・・・

従業員数 20人以下の小さい会社

とがった技術者が多いかも

ちょっと特殊なWebサービスを構築したりしている

.

.

クライアント

↑特殊HTTP(S)主に XML

サーバ

Page 5: SI屋のためのF# ~DSL編~

SI屋で働くプログラマの敵

Page 6: SI屋のためのF# ~DSL編~

SI屋で働くプログラマの敵

今日のテーマは「Excel方眼紙をいかにして倒すか」です!

Page 7: SI屋のためのF# ~DSL編~

Excel方眼紙の何が駄目か

印字切れとの闘い

ヘッダ、フッタ、目次を手動更新

自動化しにくい

そういうフォーマットを決めるのは結構だが、俺に使わせるな!

Page 8: SI屋のためのF# ~DSL編~

Excel方眼紙を触らないためには

えらい人になって、Excel方眼紙を禁止する

Excel方眼紙のない世界に移住する

Excel方眼紙は最終出力として割り切り、自動生成

Page 9: SI屋のためのF# ~DSL編~

Excel方眼紙を触らないためには

生成結果は完全でなければならない微調整があっては駄目。人手が必要な部分があると、生成物のみを編集されるようになって死ぬ。

他者をコントロールできなければならない生成物を編集することは固く禁止しないと死ぬ。

他社もコントロールできなければならない生成物を以下略。まずは自分たちで完結するドキュメントから始めるのが吉。

Page 10: SI屋のためのF# ~DSL編~

Excel方眼紙を触らないためには(2)

Excel方眼紙だけではなく、プログラムに必要なものも生成する

ツールロックインさせてしまう

可能であれば開発プロセスに組み込んでしまう

Page 11: SI屋のためのF# ~DSL編~

内部DSLの例:ログ一覧

システムが出力するログをまとめたドキュメントが必要ログには.NET標準の仕組み (トレース)を拡張したものを使用

ログ定義 XMLから、カスタムツールでログ出力用のコードを出力

ドキュメントとログ定義XMLを 1ソースから生成できると便利!.

.

DSLドキュメント

ログ定義 XML

Page 12: SI屋のためのF# ~DSL編~

DSLの作り方

あくまで一例

1. 必要な情報の洗い出し

2. 型を作る

3. 値を作る方法を考える

4. 値を作る方法を提供する

5. 出力部分を提供する

余談:JSONの上に独自のルールを課してDSLだぜ!は、よほど単純なものでないとつらいのでは?

Page 13: SI屋のためのF# ~DSL編~

DSLの作り方:必要な情報の洗い出し

まずは、DSLがどんな情報を含まなければならないかを洗い出す

ドキュメントが必要としている情報IDログレベルログ本文 (概要と詳細)パラメータ (名前、説明)ログの説明 (自明なログには書かない)

ログ定義XMLが必要としている情報IDログ出力用メソッド名ログレベルログ本文 (概要と詳細)パラメータ (名前、型)

Page 14: SI屋のためのF# ~DSL編~

DSLの作り方:型を作る

F#なのでレコードとか判別共用体とか使う.ログ定義用DSLの型の例..

.

type Level =

Critical | Error | Warning | Information | Verbose

type Parameter = string * Type * string option

type Log = {

Id: int

Level: Level

Name: string

Summary: string

Format: string

Parameters: Parameter list

Description: string option

}

Page 15: SI屋のためのF# ~DSL編~

DSLの作り方:値を作る方法を考える

内部DSLでいいか外部DSLにするかの分水嶺

関数等を組み合わせた良い感じの書き方で値が作れる→内部DSL値をDSLに戻したい→外部DSL

内部DSLは F#の力がそのまま使えてしまう→完全に元に戻すことは不可能 (条件分岐、ループ、関数・・・)

今回は内部DSLを採用

Page 16: SI屋のためのF# ~DSL編~

DSLの作り方:値を作る方法を提供する

こんな感じで書けるように関数等を定義.ログ定義用内部DSLの例 (実際はこれのリスト)..

.

define 1 Error InvalidHeader

|> parameters

[ "header" =>

typeof<string> |> withComment "不正なヘッダ"

"value" => typeof<string> |> withComment "値" ]

|> summary "ヘッダ不正"

|> format "ヘッダ{header}(値:{value})が不正です。"

|> withComment "ヘッダが不正な場合に出力されます。"

ちなみに、この書き方を実現するために FsControlを使ってます

Page 17: SI屋のためのF# ~DSL編~

DSLの作り方:出力部分を提供する

出力先に必要な情報はすべて値に含んでいるので、あとはこれを元にごにょればOK

Excelの出力にはEPPlusを使用COMと違って Excel不要NPOIと違って生成したファイルが壊れにくいライセンス (LGPL)と xlsには対応していない点に注意

XMLは無難にXDocumentXMLの構築が C#よりはるかに楽なの、もっと言っていくべきかも?

Page 18: SI屋のためのF# ~DSL編~

ここに完成図を貼る

Page 19: SI屋のためのF# ~DSL編~

外部DSLの例:テーブル仕様書

テーブルに対するドキュメントが必要

CREATE TABLE文では情報が足りないドキュメントからCREATE TABLEを生成?

どうせプログラマが書くのなら、Excelを入力にする意味はない

DSLで書いた定義からドキュメントとCREATETABLE文を吐けばいい!

Page 20: SI屋のためのF# ~DSL編~

外部DSLを選んだ理由

今後、テーブル変更DSLを作りたい

今回のDSL+テーブル変更DSLで、今回のDSLを生成し直したい

内部DSLとして、いい表現が思い浮かばなかったというのもある

Page 21: SI屋のためのF# ~DSL編~

内部DSLとの違い

再掲:DSLの作り方の一例

1. 必要な情報の洗い出し

2. 型を作る

3. 値を作る方法を考える

4. 値を作る方法を提供する

5. 出力部分を提供する

3で外部DSLの文法を考え、4でそれを実装する気に入らなければ 3に戻ってやり直す

Page 22: SI屋のためのF# ~DSL編~

文法・機能

文法は好きなようにあまり機能を詰め込まない

汎用的にしすぎると、ドメイン特化とはいったい何だったのか、となる

Page 23: SI屋のためのF# ~DSL編~

パーサジェネレータとパーサコンビネータ

パーサコンビネータは単なるライブラリなので、手軽に始めれる

パーサジェネレータはコードを生成するので、面倒

とりあえずはパーサコンビネータでいいのではないだろうか

Page 24: SI屋のためのF# ~DSL編~

F#のパーサコンビネータ

FParsec

XParsec(ない)

ParsecClone

FsAttoparsec

FParsecが機能的にも速度的にも無難

Page 25: SI屋のためのF# ~DSL編~

デモ

Page 26: SI屋のためのF# ~DSL編~

まとめ

Excel方眼紙はプログラマが触るべきものではない

Excel方眼紙の生成をプロセスに組み込んでしまう

DSLどんどん作って、Excel書く仕事なくしていこう!

Page 27: SI屋のためのF# ~DSL編~

おわり