Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dhall: Haskellの新たなキラーアプリ

C468e05d33cced21afcfe2faa69828c1?s=47 syocy
November 10, 2018
2.8k

Dhall: Haskellの新たなキラーアプリ

Dhall: A new killer application of Haskell

Presented in Haskell Day 2018 in Tokyo

C468e05d33cced21afcfe2faa69828c1?s=128

syocy

November 10, 2018
Tweet

Transcript

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

  2. このスライドについて スライドおよびソースコードは GitHub で管理してい ます https://github.com/syocy/haskell-day-syocy PDF は Releases にあります

    スライド中の Dhall コードにはすべて公式フォーマッタ をかけています
  3. Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 設定ファイルとは

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

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

  6. Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 Dhallとは

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

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

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

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

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

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

    型などを意識しなくても目的とする YAML はできた ユースケースによってはこれくらいでもまあ便利 さらに Dhall の能力を引き出すなら、型やデフォルト値 などを用意することができる 基本的なアイデアとしては、 Union 型を用いて記述できる値を制限する Record 型でデフォルト値を用意する 17 / 31
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. まとめ Dhall は設定ファイルとしての限界を突き詰めた言語 インポート・型・関数などの機能を持ちながら、 副作用や無限ループの危険がない YAML/JSON に変換できるため、既存資産に組み入れ やすい Mac, Linux

    にはバイナリ配布があり導入もしやすい ユースケースに合わせたレベルで利用できる インポートとデフォルト値があればいい ばりばり厳密に型を定義したい、など
  26. Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 補遺:

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

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

    多相な関数 型の型(カインド) 28 / 31
  29. Dhall とは 例題: Kubernetes の YAML を書いてみる 補遺: より高度な使い方 HaskellでDhallを直接読み込む

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

    Haskell によって Dhall のビルトイン関数を追加するこ とができる 標準 Dhall にはできないダーティなこともできるかも? IO 副作用とか ただし、標準ツールチェインの恩恵を受けられなくな ることに注意 標準ツールチェインも改造すればいいが大変 30 / 31
  31. 事例紹介 跋扈していた混沌の JSON 1〜3 文字の略語キー: x,o,pw,.. ヒントのない列挙型の値: 1,2,3,.. 独自マクロがあるが、それを書くと JSON

    として invalid になる Dhall で定義し直して上記の問題を解決した invaid JSON を作るために dhal-to-json は使えなかった ため、Haskell で変換処理を書いた