Elm語言

Elm是一個領域特定程式語言，用於聲明式地創建基於web瀏覽器的圖形使用者界面。Elm是純函數式的，開發它時強調了易用性、性能和健壯性。它宣傳為「實際上沒有運行時間異常」，Elm編譯器的靜態類型檢查使之成為可能。

## 歷史

Elm最初由Evan Czaplicki在2012年作為畢業論文《Elm：用於函數式GUI的並發FRP而設計的。Elm的首次發行帶有很多例子和一個在線編輯器，使得易於在web瀏覽器中試驗它。Evan在2013年加入Prezi從事Elm的工作，並在2016年轉移到NoRedInk作為開源工程師，啟動了Elm軟體基金會。

Elm編譯器的最初實現執行目標為HTML、CSS和JavaScript。核心工具集持續的擴展，現在包括了REPL、包管理器、時間旅行調試器和針對macOS及Windows的安裝器。Elm還有一個生態系統，包括社區創建的庫和Ellie，它是一個高級在線編輯器，允許保存工作和包含社區庫。

## 特徵

Elm有一個小集合的語言構造，包括傳統的if表達式，let表達式用於局部狀態，和case表達式用於模式匹配。作為函數式語言，它預設的支持匿名函數，函數作為實際參數，和部份應用。它的語義包括不可變的值，無狀態函數，和具有類型推論的靜態類型。Elm程序通過虛擬的DOM呈現HTML，還可以使用「JavaScript作為服務」來與其他代碼進行互操作。

### 不可變性

在Elm中所有的值都是不可變的，這意味著一個值不能在創建之後修改。Elm使用持久性數據結構來實現它的`Array`、`Dict`和`Set`庫。

### 靜態類型

Elm是靜態類型的。類型標註（annotation）是可選的（由於有類型推論）但強烈鼓勵。標註存在於定義之上的一行（不同於C家族語言，這裡的類型和名字是夾雜在一起的）。Elm使用單一的冒號表達「擁有類型」。

類型包括原始類型如整數和字串，和基本數據結構比如列表、元組和記錄。函數擁有用箭頭寫成的類型，例如`round : Float -> Int`。定製類型允許編程者建立定製類型，以匹配特定問題領域的方式來表示數據。

類型可以推論出其他類型，例如`List Int`。類型總是首字母大寫；小寫名字是類型變量。例如，`List a`是未知類型的值的列表。它是空列表和給`List.length`的實際參數的類型，對於這個列表的元素而言它是不可知的。有一些特殊類型，編程者建立用來與Elm運行時進行交互。例如，`Html Msg`表示（虛擬）DOM樹，其事件處理類型`Msg`的所有產生消息。

不再允許任何值是隱含的可空值（比如JavaScript的`undefined`或空指針），Elm的標準庫定義了`Maybe a`類型。產生或處理一個可選值的代碼不顯式的使用這個類型，而所有其他代碼得到保證聲稱了類型的值是實際上存在的。

Elm提供有限數目的內建類型類：`number`，它包括`Int`和`Float`，用來利用數值算符比如`(+)`或`(*)`；`comparable`，它包括數值、字符、字串、可比較者的列表和可比較者的元組，用來利用比較算符；和`appendable`，它包括字串和列表，用來利用`(++)`進行串接。Elm不提供將定製類型包括入這些類型類，或建立新類型類的機制（參見限制章節）。

### 模塊系統

Elm擁有模塊系統來允許用戶將其代碼分解到叫做模塊的更小的部份之中。模塊可以隱藏實現細節比如幫助函數，並組織有關的代碼在一起。模塊為可導入代碼充當名字空間，比如`Bitwise.and`。第三方庫（或包）構成自一個或多個模塊，並可從Elm公共庫中獲得到。所有的庫都採用語義版本號，這是編譯器和其他工具所強制的。就是說，移除一個函數或改變它的類型只能在主要發行中進行。

### 同HTML、CSS和JavaScript的互操作

Elm使用叫做埠的抽象來與JavaScript通信。它允許值流入和流出Elm程序，確使了在Elm和JavaScript之間的通信。

Elm有一個叫做elm/html的庫，編程者可以用來在Elm內書寫HTML和CSS。它使用虛擬DOM方式來使更新有效率。

### 後端

Elm官方上不支持伺服器端開發。核心開發團隊不將它作為主要目標考慮，並且偏好聚焦於在增進前端開發體驗上的開發。儘管如此，有一些獨立的計劃，它們嘗試探索將Elm用於後端的可能性。這些計劃主要系附於Elm版本0.18.0，因為更新的版本不支持「原生」代碼和其他可利用特徵。有兩個嘗試將Elm用在BEAM（Erlang虛擬機）之上。其中一個計劃直接在這個環境上執行Elm，而另一個計劃把它編譯成Elixir。還有一個嘗試通過Node.js下部結構為Elm建立後端框架。這些計劃都沒有準備好用於生產。

## Elm架構

Elm架構是建造交互式web應用的模式。Elm應用本質上以這種方式來構造，但是其他項目可能發現這個概念很有用。

Elm程序總是分解成三個部份：

- 模型：這個應用的狀態。
- 視圖：一個把模型轉變成HTML的函數。
- 更新：一個基於消息更新模型的函數。

這些是Elm架構的核心。

例如，想像顯示一個數值和在按下時增加這個數值的一個按鈕的一個應用。在這種情況下，所有我們需要存儲的是一個數值，所以我們的模型可以簡單的就是`type alias Model = Int`。`view`函數將用`Html`庫來定義並顯示這個數值和按鈕。為了讓這個數值被更新，我們需要能夠向`update`函數發送消息，這是通過定製類型比如`type Msg = Increase`來完成的。`Increase`值被附著於在`view`函數內定義的按鈕，使得在使用者點擊這個按鈕的時候，`Increase`被傳遞到`update`函數之上，它可以通過增加這個數值來更新這個模型。

在Elm架構中，發送消息至`update`是改變狀態的唯一方式。在更加複雜的應用中，消息可以來自各種來源：使用者交互，模型初始化，來自`update`的內部調用，訂閱的外部事件（窗口改變大小、系統時鐘、JavaScript互操作等等）和URL變更及請求。

## 限制

Elm不支持高種類多態，這是同為函數式的語言Haskell和PureScript所提供的，Elm還不支持創建類型類。

這意味著，例如Elm沒有跨越多種數據結構如`List`和`Set`的通用的`map`函數。在Elm中，這種函數典型的要限定上它們的模塊名字來調用，例如調用`List.map`和`Set.map`。在Haskell或PureScript中，只有一個函數`map`。自從2015年這就是在Czaplicki的粗略路線圖上的一個周知的特徵要求。

另一個缺陷是在中到大型項目中有大量的樣板代碼，如《Elm in Action》作者在他們的單一頁面應用例子中所展示的那樣，具有幾乎同樣的片段被重複於更新、視圖、訂閱、路由解析和建造函數之中。

## 樣例代碼

下面的例子代碼通過注釋展示了Elm的基本特徵：

```elm
-- 这是一个单一行注释。

{-
这是一个多行注释。
它是 {- 可嵌套的。 -}

-- 这里定义叫做greeting的一个值。类型被推论为String。
greeting =
    "Hello World!"

 -- 对顶层声明最好增加类型标注。
hello : String
hello =
    "Hi there."

-- 函数以相同方式声明，具有跟随在函数名字后的实际参数。
add x y =
    x + y

-- 再次的，最好增加类型标注。
hypotenuse : Float -> Float -> Float
hypotenuse a b =
    sqrt (a^2 + b^2)

-- 函数可以柯里化；这里我们柯里化乘法中缀算符于数2之上。
multiplyBy2 : number -> number
multiplyBy2 =
    (*) 2

-- If表达式用于在Bool值上的分支。
absoluteValue : number -> number
absoluteValue number =
    if number < 0 then negate number else number

 -- 记录用来持有命名字段。
book : { title : String, author : String, pages : Int }
book =
    { title = "Steppenwolf"
    , author = "Hesse"
    , pages = 237 
    }

-- 记录访问通过 . 来进行。
title : String
title =
    book.title

-- 记录访问 . 也可以用作一个函数。
author : String
author =
    .author book

-- 可以通过type关键字建立标签联合。
-- 下列值表示一个二叉树。
type Tree a
    = Empty
    | Node a (Tree a) (Tree a)

-- 可以用过case表达式检测这些类型。
depth : Tree a -> Int
depth tree =
    case tree of
        Empty ->
            0

        Node value left right ->
            1 + max (depth left) (depth right)
```

## 參見

- PureScript：一個強類型的、純函數式的編譯成JavaScript的程式語言。
- Reason：OCaml的語法擴展和工具鏈，也可以轉譯成JavaScript。
- 函數式反應式編程

## 基本資料

- 日語原文：Elm
- 編程范型：函數式
- 發行日期：2012年3月30日
- 軟體版本週期：0.19.1
- 軟體版本週期：2019年10月21日
- 受影響於：Haskell, Standard ML, OCaml, F#
- 施影響於：Redux, Vuex
- 類型系統：靜態, 強類型, 類型推論
- 設計者：Evan Czaplicki
- 許可證：寬鬆許可證 (三條款BSD許可證)
- 文件擴展名：.elm