特徴量エンジンKaskadaの宣言型クエリ言語Fenl〜シンタックス

# はじめに

先日、以下の記事を発表しました。

https://qiita.com/yoshiyuki_kono/items/7dc7bdad5e02ff986f5d

新しいテクノロジーに触れる時はいつでも楽しいものです。

まだまだ勉強中ですが、公式ドキュメントの学習過程の記録として、以下の記事をまとめてみました。

本稿情報のソースとして、下記ドキュメントを参照ください。

https://kaskada.io/docs-site/kaskada/main/fenl/syntax.html

# 構文
Fenl は式で構成されます。すべての Fenl 式は値のストリームを記述します。

また、式を合成して新しい式を形成することができます。


## 単項演算と二項演算
算術演算は、ほとんどのプログラミング言語と同様に表現できます。

```javascript
1 + 2 / (3 * 4)
```

論理演算では標準的な比較記号とともに、`and`および `or`というキーワードを組み合わせることができます 。

```javascript
true and (5 >= 2) or !false
```

## ドット構文

以下のようなJSONスタイルで複数の値を構成することができます。

```javascript
{ a: 10, b: true }
```

複数の値を構成するデータ型では、ドット構文を使用して個々の値を参照できます。

```javascript
{ a: 10, b: true }.a
```

上記の結果は、`10`になります。

## 条件分

すべての Fenl 式は値を生成します（Fenlは、命令型ではなく宣言型であることを思い出してください）。
`if`や`when`のような条件文は、式の値を決定するために使用されます。

たとえば、次の式の結果は、`5`という値です。

```javascript
5 | if(true)
```


そして、次の式の値は`null`になります。

```javascript
5 | if(false)
```

## Let バインディング
Let バインディングは、式に（ローカル）名を割り当てる特別な操作です。
これは、特定の式を複数回参照する必要がある場合や、複雑な式をより小さな部分に分割する場合に有益です。

バインディングには 2 つのコンポーネントがあります。名前と式のバインディングの後に、バインドされた名前を参照できる本体式が続きます。

```javascript
let the_answer = 42
in the_answer * 2
```

`let`の部分を繰り返すことによって、複数の（入れ子の）バインディングを導入することができます。バインディングは以前のバインディングを参照することができます。

```javascript
let the_answer = 42
let not_the_answer = the_answer * 2
in the_answer != not_the_answer
```

`let` 式で定義した値は、`in`式で利用されます。

## パイプ構文
Fenl では、パイプ演算子`|`を使用して関数呼び出しを連鎖させることができます。パイプ演算子の基本的な構造としては、左側の式の結果を、右側の式内の`$input`にバインドします。

```javascript
42 | mul(2, $input)
```

上記の省略表現として、下記のような表現が可能です。パイプ式の 右項内で使用される関数では、`$input`を使用する引数の記載を省略することができます。

```javascript
42 | mul(2)
```

パイプ演算子は、以下のように、複数の操作を順番に適用する場合に便利です。

```javascript
the_answer
  | add(10)
  | div(2)
  | gt(0)
```

パイプ操作`LHS | RHS`は、 `let $input = LHS in RHS`と同等と言うことも言えます。

(RHS = right-hand side, LHS = left-hand side)

## 関数呼び出し
関数は括弧を付けて呼び出されます。関数パラメータには名前が付けられます。

```javascript
substring(s = "input", start = 0, end = 2)
```

パラメーターには、デフォルト値を持つものがあります。デフォルト値を持つパラメータは関数呼び出しの記載から省略できます。

```javascript
substring("input")
```

名前付きパラメータの値をデフォルト値で指定する場合は、パラメータ名を含める必要があります。


```javascript
substring("input", 0) # 無効なシンタックス
substring("input", start = 0) # 有効なシンタックス
```


## 関数の設計
Fenlでは、多くの関数が「`data`」パラメータと「`config`」パラメータを持つ設計となっています。

たとえば、`substring`関数では、開始オフセットから終了オフセット (「`config`」パラメータ) までの文字範囲指定により、文字列 (「`data`」パラメータ) を変換します。

```
substring(zipcode, start=0, end=6)
```

Fenl の関数は、「`data`」パラメータを最後の必須パラメータまたは位置パラメータとして配置しようとします。「`data`」パラメーターを最後に配置すると、パイプ構文を使用するときにそれらを簡単に省略できます。


この設計の結果として、コード記述においては、「`config`」パラメーター指定が強調されます。


## 名前解決
Fenl の名前解決はコンテキストに依存します。コンテキストは通常​​、ユーザーが定義したテーブル名（`ref:tableservice[tables]`） およびビュー名（ `ref:viewservice[views]` ）のセットが存在しているところから始まります。

式で使う名前が、これらの名前と重複しないことが必要です。

`let`および`|`演算子を使用して名前をコンテキストに追加できます。これらの方法でバインドされた名前は、関連付けられた部分式 (たとえば、 `let`の`in`句や の RHS 式)でのみ表示されます。

名前は複数回バインドされる場合があります。名前が 2 回バインドされている場合、2 番目のバインディングは2 番目のバインディング後の`in`部分式内でのみ利用できます。

```javascript
let current_favorite = "pizza"
let first_favorite = current_favorite
let current_favorite = "hot dogs"

in { current_favorite, first_favorite }
```

一旦、名前Aによってバインドされた名前Bは、その後名前Aが新たにバインドされても、影響を受けません。
この例では、名前`current_favorite`は、別の名前`first_favorite`の宣言時に、その値としてバインドされ 、その後、名前`current_favorite`は 別の値にバインドされます。しかし、`first_favorite`の値は、`current_favorite` の再バインドの影響を受けません。 

```javascript
{ "current_favorite": "hot dogs", "first_favorite": "pizza"}
```