# Erg 部分設計的原因

[![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/faq_syntax.md%26commit_hash%3De56ba70c7d77bf2b37db77cc5bd4b21ad528531c)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/faq_syntax.md&commit_hash=e56ba70c7d77bf2b37db77cc5bd4b21ad528531c)

## 為什么類型參數要大括號 || 而不是 <> 或 []?

這是因為 `<>` 和 `[]` 會導致語法沖突

```python
# []版
id[T: Type] [t]: [T] = t
y = id[Int] # 這是一個功能嗎?
# <>版
id<T: Type> {t: T} = t
y = (id<Int, 1> 1) # 這是一個元組嗎?
# {}版
id{T: Type} {t: T} = t
y = id{Int} # 這是一個功能嗎?
# ||版
id|T: Type| t: T = t
y = id|Int| # OK
```

## {i=1} 的類型為 {i=Int}，但在 OCaml 等環境中為 {i: Int}。為什么 Erg 采用前者的語法?

Erg 設計為將類型本身也視為值

```python
A = [Int; 3]
assert A[2] == Int
T = (Int, Str)
assert T.1 == Str
D = {Int: Str}
assert D[Int] == Str
S = {.i = Int}
assert S.i == Int
```

## 你打算在 Erg 中實現宏嗎?

目前沒有。宏觀大致分為四個目的。第一個是編譯時計算。這在 Erg 中由編譯時函數負責。第二，代碼執行的延遲。這可以用 do 塊來代替。第三個是處理通用化，對此多相關數和全稱類型是比宏觀更好的解決方案。第四個是自動生成代碼，但這會造成可讀性的下降，所以我們不敢在 Erg 中實現。因此，宏的大部分功能都由 Erg 型系統承擔，因此沒有動力進行部署

## 為什么 Erg 沒有異常機制?

因為在許多情況下，使用 `Result` 類型進行錯誤處理是更好的解決方案。`Result` 類型是相對較新的編程語言中使用的常見錯誤處理技術

在 Erg 中，`?` 運算符使編寫無錯誤

```python
read_file!() =
    f = open!("foo.txt")? # 如果失敗則立即返回錯誤，所以 f 是文件類型
    f.read_all!()

# 也可以使用 try 過程捕獲類似的異常
try!:
    do!:
        s = read_file!()?
        print! s
    e =>
        # 發生錯誤時執行的塊
        print! e
        exit 1
```

在引入 Python 函數時，缺省情況下，所有函數都被視為包含異常，返回類型為`Result`。如果你知道不調度異常，請在`assert`中指明

此外，Erg 沒有引入異常機制的另一個原因是它計劃引入并行編程的功能。這是因為異常機制與并行執行不兼容(例如，如果并行執行導致多個異常，則很難處理)

## Erg似乎刪除了被認為是糟糕實踐的Python特性，為什么不取消繼承呢？

這是因為Python庫中的類是在假定它們將被繼承的基礎上設計的，如果完全取消繼承，那么它們的操作就會出現問題

然而，在Erg中，默認情況下類是final的，原則上禁止多重/多級繼承，因此可以相對安全地使用繼承

## 為什么多相關數的子類型推理默認指向記名trait?

默認情況下，指向結構托盤會使類型指定變得復雜，并且可能會混合程序員的非預期行為

```python
# 如果 T 是結構Trait的子類型...
# f: |T <: Structural Trait {.`_+_` = Self.(Self) -> Self; .`_-_` = Self.(Self) -> Self}| (T, T) -> T
f|T| x, y: T = x + y - x
# T 是名義Trait的子類型
# g: |T <: Add() and Sub()| (T, T) -> T
g|T| x, y: T = x + y - x
```

## Erg 是否實現了定義自己的運算符的功能?

A: 沒有那個計劃。最重要的原因是，如果允許定義自己的運算符，就會出現如何處理組合順序的問題。可以定義自己的運算符的 Scala 和 Haskell 等都有不同的對應，但這可以看作是可能產生解釋差異的語法的證據。此外，獨立運算符還有一個缺點，那就是可能產生可讀性較低的代碼

## 為什么 Erg 取消了 += 這樣的賦值運算符?

首先，Erg 中沒有變量可變性。換句話說，它不能被重新分配。一旦一個對象綁定到一個變量，它就會一直綁定到該變量，直到它超出范圍并被釋放。Erg 中的可變性意味著對象可變性。一旦你知道了這一點，故事就很簡單了。例如，`i += 1` 表示 `i = i + 1`，但這樣的語法是非法的，因為變量沒有被重新分配。Erg 的另一個設計原則是操作符不應該有副作用。Python 大多是這樣，但是對于某些對象，例如 Dict，賦值運算符會改變對象的內部狀態。這不是一個非常漂亮的設計
這就是賦值運算符完全過時的原因

## 為什么 Erg 在語法上特別對待有副作用的過程?

副作用的局部化是代碼維護的一個關鍵因素

但是，確實也不是沒有方法可以不在語言上特殊對待副作用。例如，可以用代數效果(類型系統上的功能)替代過程。但這樣的合一并不總是正確的。例如，Haskell 沒有對字符串進行特殊處理，只是一個字符數組，但這種抽象是錯誤的

什么情況下，可以說合一化是錯的?一個指標是"是否會因其合一而難以看到錯誤信息"。Erg 設計師發現，將副作用特殊處理會使錯誤消息更容易閱讀

Erg 有一個強大的類型系統，但并不是所有的類型都決定了它。如果這樣做了，你的下場就跟 Java 試圖用類來控制一切一樣
