# 可変型

[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/type/18_mut.md%26commit_hash%3D06f8edc9e2c0cee34f6396fd7c64ec834ffb5352)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/type/18_mut.md&commit_hash=06f8edc9e2c0cee34f6396fd7c64ec834ffb5352)

> __Warning__: この項の情報は古く、一部に間違いを含みます。

Ergではデフォルトですべての型が不変型、すなわち内部状態を更新できないようになっています。
しかし可変な型ももちろん定義できます。可変型は`!`を付けて宣言します。

```python
Person! = Class({name = Str; age = Nat!})
Person!.
    greet! ref! self = print! "Hello, my name is {self::name}. I am {self::age}."
    inc_age! ref! self = self::name.update! old -> old + 1
```

正確には、可変型・または可変型を含む複合型を基底型とする型は型名の最後に`!`を付けなくてはなりません。`!`を付けない型も同一の名前空間に存在してよく、別の型として扱われます。
上の例では、`.age`属性は可変で、`.name`属性は不変となっています。一つでも可変な属性がある場合、全体として可変型になります。

可変型はインスタンスを書き換えるプロシージャルメソッドを定義できますが、プロシージャルメソッドを持つからと言って可変型になるとは限りません。例えば配列型`[T; N]`には要素をランダムに選ぶ`sample!`メソッドが実装されていますが、これはもちろん配列に破壊的変更を加えたりはしません。

可変型オブジェクトの破壊的操作は、主に`.update!`メソッドを介して行います。`.update!`メソッドは高階プロシージャで、`self`に関数`f`を適用して更新します。

```python
i = !1
i.update! old -> old + 1
assert i == 2
```

`.set!`メソッドは単に古い内容を捨てて新しい値に差し替えます。`.set! x = .update! _ -> x`です。

```python
i = !1
i.set! 2
assert i == 2
```

`.freeze_map`メソッドは値を不変化して操作を行います。

```python
a = [1, 2, 3].into [Nat; !3]
x = a.freeze_map a: [Nat; 3] -> a.iter().map(i -> i + 1).filter(i -> i % 2 == 0).collect(Array)
```

多相不変型において型の型引数`T`は暗黙に不変型であると仮定されます。

```python
# ImmutType < Type
K T: ImmutType = Class ...
K! T: Type = Class ...
```

標準ライブラリでは、可変型`(...)!`型は不変型`(...)`型を基底としている場合が多いです。しかし`T!`型と`T`型に言語上特別な関連はなく、そのように構成しなくても構いません[<sup id="f1">1</sup>](#1)。

注意として、オブジェクトの可変性にはいくつかの種類があります。
以下では組み込みのコレクション型について、その不変/可変型の意味を確認します。

```python
# 配列型
## 不変型(immutable types)
[T; N]     # 可変操作は実行できない
## 可変型(mutable types)
[T!; N]    # 中身を1つずつ変更できる
[T; !N]    # 可変長、中身は変更不能だが要素の追加・削除で実質変更できる
[!T; N]    # 中身は不変オブジェクトだが、型を変えたものに差し替え可能できる(型を変えないという操作で実質差し替えできる)
[!T; !N]   # 型、長さを変更できる
[T!; !N]   # 中身、長さを変更でき
[!T!; N]   # 中身、型を変更できる
[!T!; !N]  # ありとあらゆる可変操作を実行できる
```

もちろんこれら全てを暗記し、使いこなす必要はありません。
可変配列型については、可変にしたい部分に`!`を付けるだけであり、実用上は`[T; N]`, `[T!; N]`, `[T; !N]`, `[T!; !N]`の4つでほとんどのケースをカバーできます。

これらの配列型は糖衣構文であり、実際の型は以下の通りです。

```python
# 実際は4種類の型
[T; N] = Array(T, N)
[T; !N] = Array!(T, !N)
[!T; N] = ArrayWithMutType!(!T, N)
[!T; !N] = ArrayWithMutTypeAndLength!(!T, !N)
[T!; !N] = Array!(T!, !N)
[!T!; N] = ArrayWithMutType!(!T!, N)
[!T!; !N] = ArrayWithMutTypeAndLength!(!T!, !N)
```

なお、型を変更可能とはこのような意味です。

```python
a = [1, 2, 3].into [!Nat; 3]
a.map!(_ -> "a")
a: [!Str; 3]
```

他のコレクション型についても同様です。

```python
# タプル型
## 不変型(immutable types)
(T, U) # 要素数不変、中身を変更できない
## 可変型(mutable types)
(T!, U) # 要素数不変、最初の要素は変更できる
(T, U)! # 要素数不変、中身を変更できる
...
```

```python
# セット型
## 不変型(immutable types)
{T; N}        # 不変要素数、中身を変更できない
## 可変型(mutable types)
{T!; N}       # 不変要素数、中身を(1つずつ)変更できる
{T; N}!       # 可変要素数、中身は変更できないが、要素の追加削除で実質的に中の型を変更できる
{T!; N}!      # 可変要素数、中身も変更できる
...
```

```python
# 辞書型
## 不変型(immutable types)
{K: V}          # 不変長、中身を変更できない
## 可変型(mutable types)
{K: V!}         # 不変長、値を(1つずつ)変更できる
{K: V}!         # 可変長、中身を変更できないが、要素の追加削除で実質的に中の型も変更できる
...
```

```python
# レコード型
## 不変型(immutable types)
{x = Int; y = Str}           # 中身を変更できない
## 可変型(mutable types)
{x = Int!; y = Str}          # xの値を変更できる
{x = Int; y = Str}!          # {x = Int; y = Str}のインスタンスならば何でも差し替えられる
...
```

`T = (...)`のとき単に`T! = (...)!`となる型`(...)`を単純構造型と呼びます。単純構造型は(意味論上)内部構造を持たない型ともいえます。
配列、タプル、セット、辞書、レコード型は全て単純構造型ではありませんが、Int型や篩型は単純構造型です。

```python
# 篩型
## 列挙型
{1, 2, 3}    # 1, 2, 3のうちどれか、変更できない
{1, 2, 3}!   # 1, 2, 3のうちどれか、変更できる
## 区間型
1..12  # 1~12のうちどれか、変更できない
1..12! # 1~12のうちどれか、変更できる
## 篩型(一般形)
{I: Int | I % 2 == 0}  # 偶数型、変更できない
{I: Int! | I % 2 == 0} # 偶数型、変更できる
{I: Int | I % 2 == 0}! # 上と全く同じ型、だが上の記法が推奨される
```

以上の説明から、可変型とは自身が可変であるものだけでなく、内部に持つ型が可変であるものも含まれるということになります。
`{x: Int!}`や`[Int!; 3]`などの型は、内部のオブジェクトが可変であり、インスタンス自身が可変なわけではない内部可変型です。

内部構造を持ち型構築子自体に`!`がついている型`K!(T, U)`の場合、`*self`はオブジェクト全体を変えうるものです。また、局所的な変更も可能です。
ただし、変更権限はなるべく局所的に抑えることが望ましいので、変更されうるのが`T`のみの場合`K(T!, U)`とした方が良いでしょう。
また内部構造を持たない型`T!`の場合、このインスタンスは単に入れ替え可能な`T`のボックスです。メソッドによって型を変更することはできません。

---

<span id="1" style="font-size:x-small"><sup>1</sup> `T!`型と`T`型に言語上の特別な関係がないのは意図的な設計です。関連があったとすると、例えば名前空間に`T`/`T!`型が存在するときに別のモジュールから`T!`/`T`型を導入できなくなるなどの不都合が生じます。また、不変型に対し可変型は一意に定まりません。`T = (U, V)`という定義があった際、`(U!, V)`と`(U, V!)`という可変サブタイプが`T!`としてあり得えます。 [↩](#f1)</span>
