Power Query M 式言語の「..」は範囲演算子、「...」は未実装のエラー若しくはオープンレコードマーカー

# Power Query M 式言語の「..」は範囲演算子、「...」は未実装のエラー若しくはオープンレコードマーカー

## 概要
Power Query M 式言語で、勘違いしそうな記法の記事です。

Power Query M 式言語では「..」は範囲演算子、「...」は未実装のエラー若しくはオープンレコードマーカーです。
※Power Query M 式言語のLearn内では「..」演算子の名称が記載されていませんでしたが、一般的には「範囲演算子」と呼ばれていますので、以下、「..」演算子は「範囲演算子」と記載します。

ドットが一つ違うだけで、まったく別の意味になります。
これは要注意です！
以下、範囲演算子と未実装のエラーと若しくはオープンレコードマーカーが書かれているLearnです。

- Learn / Power Query M 式言語 / 一覧（「一覧」と訳されていますがリストの事です。）
https://learn.microsoft.com/ja-jp/powerquery-m/m-spec-values#list
> 一覧に連続する整数を含める目的で a..b 形式を使用できます。
> { 1, 5..9, 11 }     // { 1, 5, 6, 7, 8, 9, 11 }

- Learn / Power Query M 式言語 / 未実装のエラー
https://learn.microsoft.com/ja-jp/powerquery-m/m-spec-error-handling#not-implemented-error
> 省略記号 (...) は、error のショートカットとして使用できます。
> (x, y) => if x > y then x - y else ...

- Learn / Power Query M 式言語 / オープンレコードマーカー
https://learn.microsoft.com/ja-jp/powerquery-m/m-spec-types#record-types
> open-record-marker:
>      ...

## なぜ、勘違いしていたか
Ruby では、「..」と「...」は共に範囲演算子なので、私自身、M式言語を触りはじめた当初、同じだろうと勘違いをしていました。
M式言語の記法に「..」と「...」があるなら、両方とも範囲演算子に違いない、と。

しかし、実際にはM式言語では「...」は未実装のエラーでした。
Python も「...」は未実装の意味なので、似た感じですね。

今回、記事を書いた理由は、同じように勘違いをしている方のお役にたてればと思い書きました。

## M式言語での使い方の例

M式言語での範囲演算子と未実装のエラーの使い方の例です。
※ Power BI バージョン: 2.138.1203.0 64-bit (2024年11月)で検証しました。

### 範囲演算子
#### 数字のリスト
```
= {1..10}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55445/2c50a148-2251-1ef8-330f-bac3bdae5350.png)

#### 数字のリスト（飛び飛び）
```
= {1..3, 8..10}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55445/7232b598-7c81-97f6-bf58-f5a7ce55e444.png)


### 文字のリスト（アルファベット）
text型も使えます。
```
= {"a".."d"}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55445/6fc0308c-6ab7-dcbf-154f-dc386711d196.png)

### 文字のリスト（日本語）
日本語も使えます。
```
= {"あ".."お"}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55445/784394bd-f8f3-359c-5341-f9553342a116.png)
### 文字の並び順

上記の日本語の並び順から、ユニコードのコード順に出力しているようです。

https://en.wikipedia.org/wiki/Hiragana_(Unicode_block)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55445/3fe74776-a886-ad10-0f20-d3b84099813d.png)

### NGパターン
number型とtext型以外の方はエラーでした。

## 未実装のエラー

これは、Learn の未実装の項目に書かれているとおりですね。
https://learn.microsoft.com/ja-jp/powerquery-m/m-spec-error-handling#not-implemented-error

```
= ...
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55445/b2940a72-686d-325d-f4f2-9031754609a9.png)
下記と同じですね。
```
= error Error.Record("Expression.Error", "値が指定されませんでした。")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55445/77c93040-eee2-805e-b264-a7e563e8d2da.png)

## オープンレコードマーカー

オープンレコードマーカーに関してLearnで取り上げられてるのは以下だけで、例題は載っていませんでした。

- Learn / Power Query M 式言語 / オープンレコードマーカー
https://learn.microsoft.com/ja-jp/powerquery-m/m-spec-types#record-types
> open-record-marker:
>      ...

Webや書籍を調べてみたのですが、オープンレコードマーカーに関して取り上げている記事がなく、詳しく書かれているのは以下くらいでした。

https://stackoverflow.com/questions/40883574/purpose-and-usage-of-open-record-type-in-m-power-query-formula-language

使い方が載っていたので、手元のPCで動かしてみました。
```
let
    Record1 = [x = 1, A = 3, B = 4],
    RecordType = type [x = number, optional y = text,...],
    RecordTypeFields = Type.RecordFields(RecordType),
    ToTable = Record.ToTable(RecordTypeFields),
    RecordTypeTable = Table.ExpandRecordColumn(ToTable, "Value", {"Optional", "Type"}, {"Optional", "Type"}),
    RecordTable = Table.FromColumns({Record.FieldNames(Record1),Record.FieldValues(Record1)},{"Record FieldName", "Record FieldValue"}),
    JoinedTable = Table.Join(RecordTypeTable, "Name", RecordTable, "Record FieldName", JoinKind.FullOuter),
    ConformityCheck = Table.AddColumn(JoinedTable, "Conform", 
                        each if [Optional] = null then Type.IsOpenRecord(RecordType) else
                             if [Optional] then true else
                             if [Record FieldValue] <> null then Value.Is([Record FieldValue], [Type]) else
                             false),
    Result = List.AllTrue(ConformityCheck[Conform])
in
    Result
```

> It's true, there aren't a lot of uses for open record types in "M" today.

> One place where it could have been useful is if we had the concept of "ragged" tables, e.g. this CSV with two, three, and four fields of data in the different rows.

> 確かに、今日「M」においてオープン・レコード・タイプの使い道は多くない。 

> 役に立ったかもしれない場所のひとつは、「ラグド」テーブルの概念があればということだ。例えば、このCSVの異なる行に2つ、3つ、4つのフィールドのデータがある。（DeepL にて自動翻訳）

> A,B,C
> 1,2
> 1,2,3
> 1,2,3,4

具体的な使用場面としては、読み込んだデータの行が凸凹だった場合に使うものらしいです。

その他、参考になりそうな記事
https://powerquery.how/type-openrecord/
> // Output: type [ Date = date, Amount = number, ... ]
> Type.OpenRecord( type [ Date = date, Amount = number ] )
> This expression effectively converts the initially closed record type into an open one, represented by the open-record-marker (...) to indicate its new open nature.

> この式は、最初にクローズされたレコード・タイプをオープン・タイプに効果的に変換し、その新しいオープンな性質を示すオープン・レコード・マーカー（...）で表される。（DeepL にて自動翻訳）

Type.OpenRecord()と同じ機能と書かれていますね。

Type.OpenRecord()のLearnを調べると同じ似た事が書かれていました。

https://learn.microsoft.com/ja-jp/powerquery-m/type-openrecord

当記事ではオープン・レコード・タイプについては、ここまでとします。
（別記事で公開するかもです。）

## まとめ

Power Query M 式言語では、ドットが一つ違うだけで、まったく別の意味になりますので注意です！

## 参考サイト
Learn / Power Query M 式言語 / 未実装のエラー
https://learn.microsoft.com/ja-jp/powerquery-m/m-spec-error-handling#not-implemented-error