Article:

yispはjsonnetやcueなどと同様、データ記述言語の1つです。「イスプ」と読みます。yaml+lispでyispです。

yisp既存のデータ記述言語と異なり、①構文がyamlそのままであること、②lisp-likeな評価機構をもっていること の2点が特徴です。

主な用途としてはkubernetesのマニフェストやGrafanaのダッシュボードなどの設定ファイルを生成するために活用できますが、その他yaml及びjsonを設定ファイルとして利用するシステムにおいて、yispを用いることでより柔軟な設定が可能になります。

基本

最もシンプルやyispの例は次のようになります。

helloworld.yaml
1
2
3
4
5
mystring: !yisp
  - concat
  - hello
  - ' '
  - world

yamlのタグ機能を使って、生データの任意の場所から「ここから評価を行う」ということを指示します。

これをyispで評価します。

1
yisp build helloworld.yaml

すると、次のyamlが出力されます。

result
1
mystring: "hello world"

この例では、

  • !yispの外側の構造はそのままyamlとして出力されている
  • !yispの中身はlisp-likeな構文で書かれ、concat(hello, ’ ‘, world)が評価されている

の2点が重要です。

つかえる演算子のリストはドキュメントにありますが、実装からドキュメント反映にラグがある場合があるのでコードのここを見た方が早かったりします。

モジュール

他のファイルを名前付きでインポートすることができ、モジュールのように利用することができます。 例えば次のようなテンプレートを定義したファイルを作ります。

template.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
!yisp &mkpod
- lambda
- [name, image]
- !quote
  apiVersion: v1
  kind: Pod
  metadata:
    name: *name
  spec:
    containers:
      - name: *name
        image: *image

import構文をつかってこのファイルを読み込み、*template.mkpodで呼び出します。

template-use.yaml
1
2
3
4
5
6
7
8
!yisp
- import
- ["template", "./template.yaml"]
---
!yisp
- *template.mkpod
- mypod1
- myimage1

こうして評価すると、次のようなyamlが出力されます。

result
1
2
3
4
5
6
7
8
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
spec:
  containers:
    - name: mypod1
      image: myimage1

yispではjsonschemaのような形式型オブジェクトを作成し、型チェックやキャストを行うことができます。

たとえば次のようなyamlを書きます。

type.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
!yisp &Person
- schema
- !quote
  type: object
  properties:
    name:
      type: string
    age:
      type: integer
  required:
    - name
    - age

---
!yisp &FormatPerson
- lambda
- [!Person person] #引数に!Personタグを付けて型を指定。型名はアンカーで定義したものも参照できる。
- - format
  - "%s is %d years old"
  - *person.name
  - *person.age

---
!yisp
- *FormatPerson
- name: "John Doe"
  age: 30

これは1つめのドキュメントでPerson型を定義し、2つめのドキュメントでその型を受け取る関数FormatPersonを定義しています。

実行結果はつぎのようになります。

John Doe is 30 years old

次に、たとえばageの値を文字列にしてしまった状態で実行してみます。

wrong-age-type.yaml
1
2
3
4
!yisp
- *FormatPerson
- name: "John Doe"
  age: "30"

すると、intを期待していたのにstringが来たというエラーが出力されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
> 06/08 05:58 ~/git/yisp$ go run . build test.yaml
Error: object does not satisfy type (expected int, got string)

/home/totegamma/git/yisp/test.yaml
==================================
23 |---
24 |!yisp
25 |- *FormatPerson
26 |- name: "John Doe"
      ^ object does not satisfy type (expected int, got string)
27 |  age: "30"
28 |

Traceback:
object does not satisfy type (expected int, got string) at /home/totegamma/git/yisp/test.yaml:26:3
failed to apply function at /home/totegamma/git/yisp/test.yaml:24:1
> 06/08 05:59 ~/git/yisp$

ageの入力をそもそもなくすと、次のエラーが表示されます。

1
Error: object does not satisfy type (missing required property: age)

また、型チェックは関数呼び出しだけでなくassert-typeオペレーターでも行えます。

1
2
3
4
5
!yisp
- assert-type
- *Person
- name: "John Doe"
  age: "30"

go-runによる外部プラグイン呼び出し

yispは非常に強力な表現力を持っていますが、ありとあらゆるロジックをyispで書くのは旨味が薄いです。 そのため、yispではgo-runオペレーターを使ってGoのパッケージをcil呼び出しすることができます。

例えばhelmchartからマニフェストを生成する例は次のようになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
!yisp &helm-template
- lambda
- [props]
- - go-run
  - pkg: github.com/totegamma/yisp-helm-adapter@v0.1.0
    args: !quote
      - *props.repo
      - *props.release
      - *props.version
    stdin:
      - to-yaml
      - *props.values

---
!yisp
- *helm-template
- repo: "https://charts.concrnt.net/"
  release: "concrnt"
  version: "0.7.13"
  values:
    meilisearch:
      enabled: true

最初の例では簡単のためにhelm-template関数を同じファイル内で定義していましたが、オススメはGoのレポジトリに含めておき、ネットワークリソースをimportして使うことです。 たとえば、yisp-helm-adapterであればこのようにimportして呼び出すことができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
!yisp
- import
- ["helmchart", "https://raw.githubusercontent.com/totegamma/yisp-helm-adapter/refs/heads/main/helm-chart.yaml"]

---
!yisp
- *helmchart.template
- repo: "https://charts.concrnt.net/"
  release: "concrnt"
  version: "0.7.13"
  values:
    meilisearch:
      enabled: true

エキシビション

yispは(必要以上に)lispとして実装されているので、例えば不動点コンビネーターの一つであるZコンビネーターによる無名再起のプログラムなども正常に動作します。

z-combinator.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
!yisp
&Z
- lambda
- [f]
- - - lambda
    - [x]
    - - *f
      - - lambda
        - [v]
        - - - *x
            - *x
          - *v
  - - lambda
    - [x]
    - - *f
      - - lambda
        - [v]
        - - - *x
            - *x
          - *v
---
!yisp
- - *Z
  - - lambda
    - [self]
    - - lambda
      - [n]
      - - if
        - [<=, *n, 1]
        - 1
        - - "*"
          - *n
          - - *self
            - - "-"
              - *n
              - 1
- 5
# => 120

その他記事

珍しく自ブログ以外でも照会記事を書いているので、よければそちらも併せてどうぞ。

qiita
dev.to(英語!)