Slide 1

Slide 1 text

Dhall: Haskellの新たなキラーアプリ @syocy 2018-11-10

Slide 2

Slide 2 text

このスライドについて スライドおよびソースコードは GitHub で管理してい ます https://github.com/syocy/haskell-day-syocy PDF は Releases にあります スライド中の Dhall コードにはすべて公式フォーマッタ をかけています

Slide 3

Slide 3 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 設定ファイルとは コンパイルなどの前処理をせずにプログラムのパラ メータを変えたい → パラメータをソースコードの外に切り離したい → 設定ファイル 3 / 31

Slide 4

Slide 4 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 既存の設定ファイル言語への不満 現在主流の設定ファイル言語は機能が少ない 同じ値を繰り返し書きたくない (Don’t Repeat Yourself) 入ってはいけない変な値が入っていたら教えてほしい (静的検査, 型) 大きい設定ファイルを分割したい (インポート) ただし、設定ファイルとしての領分は守ってほしい 副作用を持っていて動作が不定だったり、 無限ループを起こしてハングしたりはしないでほしい なにかいいものはないのか 4 / 31

Slide 5

Slide 5 text

Dhall

Slide 6

Slide 6 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Dhallとは 今まで挙げた特徴を備えた設定ファイル言語 型・関数・インポートの機能がある 無限ループは起こらない(チューリング完全ではない) 副作用は(標準では)ない 読みは dɔl (US) もしくは dɔːl (UK) カタカナでは「ダール」もしくは「ドール」? このスライドでは「ダール」で通します 6 / 31

Slide 7

Slide 7 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Dhallの開発体制 言語仕様は独立して管理されている https://github.com/dhall-lang/dhall-lang 主要ツール、および言語仕様の参照実装は Haskell で書 かれている https://github.com/dhall-lang/dhall-haskell 7 / 31

Slide 8

Slide 8 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Dhallの型: プリミティブな型 型 意味 リテラル (例) Bool 真偽値 True, False Natural 0 以上の整数 0, 1 Integer 符号付き整数 -1, +1 Double 小数点付き数 0.1, -2e10 Text 文字列 "", "abc" プリミティブ型には一般的なものが揃っている 数値の型はできるだけ Natural を使うのがおすすめ 使える標準関数が多いため 8 / 31

Slide 9

Slide 9 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Dhallの型: 複合型 型 意味 値の例 List 0 個以上の値の集まり [1,2,3] Optional 0〜1 個の値の集まり Some 1 Record キーと値のペアの集まり {a=1, b=2} Union 「どれかひとつ」を表す Record は JSON のオブジェクトに相当 Union の書き方が特徴的 上記の例では「A と B のラベルがある Union 型で A を 選んだ値」となる 記述をサポートする標準関数がある 9 / 31

Slide 10

Slide 10 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 インポート ローカルパスと URL からのインポートができる ローカルパスからのインポート let config = ./my/local/config.dhall URL からのインポート let config = https://example.com/config.dhall インポートにあたってハッシュ値のチェックをするこ ともできる Dhall ファイルではない raw text のインポートも可能 長い自然言語文章などに 10 / 31

Slide 11

Slide 11 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Dhallの導入は難しい? なるほど Dhall は便利かも だけど自分の使っている言語にバインディングがない† もう YAML や JSON で設定作ってしまっているし…… 本当に導入は難しいのか †今のところ最新仕様に準拠したバインディングは Haskell のみ 11 / 31

Slide 12

Slide 12 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Dhallの導入は簡単 dhall-to-yaml/dhall-to-json ‡ がある Dhall ファイルを YAML や JSON に変換するコマンドラ インツール プログラムで Dhall を読み込むことを考えなくて良い バインディングがない言語でも Dhall を使える 導入は Mac, Linux はコマンド一発、Windows は The Haskell Tool Stack を入れる必要がある ‡https://github.com/dhall-lang/dhall-lang/wiki/ Getting-started%3A-Generate-JSON-or-YAML 12 / 31

Slide 13

Slide 13 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 例題: KubernetesのYAMLを書いてみる 最近話題の OSS, Kubernetes は YAML を大量に用いる ことで有名 Wall of YAML などとも呼ばれる 実際さまざまな YAML 管理のソリューションが存在 する 今回は dhall-to-yaml を用いて安全・便利に Kubernetes の YAML を生成してみる 実はすでに dhall-kubernetes§というのがあり、本当に Kubernetes YAML を作るならそれを使う方がよい。今 回はあくまで例題として DIY する。 §https://github.com/dhall-lang/dhall-kubernetes 13 / 31

Slide 14

Slide 14 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 目的とするYAML service.yaml¶ 1 kind: Service 2 apiVersion: v1 3 metadata: 4 name: my-service 5 spec: 6 selector: 7 app: MyApp 8 ports: 9 - protocol: TCP 10 port: 80 11 targetPort: 9376 ¶https: //kubernetes.io/docs/concepts/services-networking/service/ 14 / 31

Slide 15

Slide 15 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 愚直なDhall k_notype.dhall まず型とかは考えずに愚直に書いてみる 1 { kind = 2 "Service" 3 , apiVersion = 4 "v1" 5 , metadata = 6 { name = "my-service" } 7 , spec = 8 { selector = 9 { app = "MyApp" } 10 , ports = 11 [ { protocol = "TCP", port = 80, targetPort = 93 12 } 13 } 15 / 31

Slide 16

Slide 16 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 愚直なDhallをYAMLに変換 目的とする YAML ができた! 1 # cat k_notype.dhall | dhall-to-yaml 2 apiVersion: v1 3 kind: Service 4 spec: 5 selector: 6 app: MyApp 7 ports: 8 - targetPort: 9376 9 protocol: TCP 10 port: 80 11 metadata: 12 name: my-service 16 / 31

Slide 17

Slide 17 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 よりDhallらしい方法 型などを意識しなくても目的とする YAML はできた ユースケースによってはこれくらいでもまあ便利 さらに Dhall の能力を引き出すなら、型やデフォルト値 などを用意することができる 基本的なアイデアとしては、 Union 型を用いて記述できる値を制限する Record 型でデフォルト値を用意する 17 / 31

Slide 18

Slide 18 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Kubernetes YAMLの型を定義(1/2) k_types.dhall Kubernetes YAML の型を定義するファイルを作る 1 let Kind_ = < Service : {} | Pod : {} | Deployment : 2 3 in let ApiVersion = < v1 : {} > 4 5 in let Metadata = { name : Text } 6 7 in let Selector = { app : Text } 8 9 in let Protocol = < TCP : {} | UDP : {} > 10 11 in let Port = { protocol : Protocol, port : Natural, ta 12 13 in let Spec = { selector : Selector, ports : List Port 18 / 31

Slide 19

Slide 19 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Kubernetes YAMLの型を定義(2/2) k_types.dhall Union で表現した ApiVersion などを YAML の String に戻 す処理も必要(実装は省略) 1 in let makeYaml = 2 λ ( args 3 : { kind : 4 Kind_ 5 , apiVersion : 6 ApiVersion 7 , metadata : 8 Metadata 9 , spec : 10 Spec 11 } 12 ) 19 / 31

Slide 20

Slide 20 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 定義した型を利用して書き直す k_service.dhall 1 let k = ./k_types.dhall 2 3 in k.makeYaml 4 { kind = 5 k.Kinds.Service {=} 6 , apiVersion = 7 k.ApiVersions.v1 {=} 8 , metadata = 9 { name = "my-service" } 10 , spec = 11 { selector = 12 { app = "MyApp" } 13 , ports = 14 [ { protocol = k.Protocols.TCP {=}, port = 8 15 } 16 } 20 / 31

Slide 21

Slide 21 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 型付き版をYAMLに変換 より安全に Kubernetes YAML が表現できた 1 # cat k_service.dhall | dhall-to-yaml 2 apiVersion: v1 3 kind: Service 4 spec: 5 selector: 6 app: MyApp 7 ports: 8 - targetPort: 9376 9 protocol: TCP 10 port: 80 11 metadata: 12 name: my-service 21 / 31

Slide 22

Slide 22 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 デフォルト値(1/2) k_service_default.dhall Kubernetes YAML のデフォルト値を作ってみる 1 let k = ./k_types.dhall 2 3 in let defaultService = 4 { kind = k.Kinds.Service {=}, apiVersion = k.ApiVersions.v1 {=} } 5 6 in let tcp = { protocol = k.Protocols.TCP {=} } デフォルト値を用意すると、同じような値をたくさん作る ときに間違いをしにくくなる(今回例示するのは1つ) 22 / 31

Slide 23

Slide 23 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 デフォルト値(2/2) k_service_default.dhall Record は演算子 ∧ で組み合わせることができる 1 in k.makeYaml 2 ( defaultService 3 ∧ { metadata = 4 { name = "my-service-1" } 5 , spec = 6 { selector = 7 { app = "MyApp1" } 8 , ports = 9 [ tcp ∧ { port = 80, targetPort = 9376 10 } 11 } 12 ) 23 / 31

Slide 24

Slide 24 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 デフォルト値版をYAMLに変換 もちろんこれまでと同じく YAML に変換できる 1 # cat k_service_default.dhall | dhall-to-yaml 2 apiVersion: v1 3 kind: Service 4 spec: 5 selector: 6 app: MyApp1 7 ports: 8 - targetPort: 9376 9 protocol: TCP 10 port: 80 11 metadata: 12 name: my-service-1 24 / 31

Slide 25

Slide 25 text

まとめ Dhall は設定ファイルとしての限界を突き詰めた言語 インポート・型・関数などの機能を持ちながら、 副作用や無限ループの危険がない YAML/JSON に変換できるため、既存資産に組み入れ やすい Mac, Linux にはバイナリ配布があり導入もしやすい ユースケースに合わせたレベルで利用できる インポートとデフォルト値があればいい ばりばり厳密に型を定義したい、など

Slide 26

Slide 26 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 補遺: より高度な使い方 dhall コマンドラインツール 紹介しなかった言語機能 Haskell で Dhall を直接読み込む Haskell で Dhall を拡張する 26 / 31

Slide 27

Slide 27 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 dhall コマンドラインツール このスライドではもっぱら dhall-to-yaml/dhall-to-json を使ってきた Mac, Linux だと Haskell 環境がなくても導入できるため (今のところ)Haskell 環境が必要だが dhall コマンドを 導入するとより幸せになれる 便利なサブコマンド (一部) dhall format: 公式フォーマッタ dhall repl: Dhall インタラクティブ環境 27 / 31

Slide 28

Slide 28 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 紹介しなかった言語機能 多相な関数 型の型(カインド) 28 / 31

Slide 29

Slide 29 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 HaskellでDhallを直接読み込む このスライドではもっぱら dhall-to-yaml/dhall-to-json を使って、Dhall を YAML/JSON に変換する方法を見て きた Haskell にはバインディングがあり、YAML 等を介さず に Dhall ファイルを Haskell の型として読み込むことが できる YAML/JSON 以外の形式に変換したい場合、あるいは Haskell プログラムの設定ファイルとして 29 / 31

Slide 30

Slide 30 text

Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 HaskellでDhallを拡張する Haskell によって Dhall のビルトイン関数を追加するこ とができる 標準 Dhall にはできないダーティなこともできるかも? IO 副作用とか ただし、標準ツールチェインの恩恵を受けられなくな ることに注意 標準ツールチェインも改造すればいいが大変 30 / 31

Slide 31

Slide 31 text

事例紹介 跋扈していた混沌の JSON 1〜3 文字の略語キー: x,o,pw,.. ヒントのない列挙型の値: 1,2,3,.. 独自マクロがあるが、それを書くと JSON として invalid になる Dhall で定義し直して上記の問題を解決した invaid JSON を作るために dhal-to-json は使えなかった ため、Haskell で変換処理を書いた