Elm語言
Elm是一個領域特定編程語言,用於聲明式地創建基於web瀏覽器的圖形用戶界面。Elm是純函數式的,開發它時強調了易用性、性能和健壯性。它宣傳為「實際上沒有運行時間異常」[6],Elm編譯器的靜態類型檢查使之成為可能。
編程範型 | 函數式 |
---|---|
設計者 | Evan Czaplicki |
面市時間 | 2012年3月30日[1] |
當前版本 |
|
型態系統 | 靜態, 強類型, 類型推論 |
許可證 | 寬鬆許可證 (三條款BSD許可證)[2] |
文件擴展名 | .elm |
網站 | elm-lang |
啟發語言 | |
Haskell, Standard ML, OCaml, F# | |
影響語言 | |
Redux,[3] Vuex[4] |
歷史
Elm最初由Evan Czaplicki在2012年作為畢業論文《Elm:用於函數式GUI的並發FRP》而設計的[7]。Elm的首次發行帶有很多例子和一個在線編輯器,使得易於在web瀏覽器中試驗它[8]。Evan在2013年加入Prezi從事Elm的工作[9],並在2016年轉移到NoRedInk作為開源工程師,啟動了Elm軟件基金會[10]。
Elm編譯器的最初實現執行目標為HTML、CSS和JavaScript[11]。核心工具集持續的擴展,現在包括了REPL[12]、包管理器[13]、時間旅行調試器[14]和針對macOS及Windows的安裝器[15]。 Elm還有一個生態系統,包括社區創建的庫[16]和Ellie[17],它是一個高級在線編輯器,允許保存工作和包含社區庫。
特徵
Elm有一個小集合的語言構造,包括傳統的if表達式,let表達式用於局部狀態,和case表達式用於模式匹配[18]。作為函數式語言,它缺省的支持匿名函數,函數作為實際參數,和部份應用。它的語義包括不可變的值,無狀態函數,和具有類型推論的靜態類型。Elm程序通過虛擬的DOM呈現HTML,還可以使用「JavaScript作為服務」來與其他代碼進行互操作。
不可變性
在Elm中所有的值都是不可變的,這意味着一個值不能在創建之後修改。Elm使用持久性數據結構來實現它的Array
、Dict
和Set
庫[19]。
靜態類型
Elm是靜態類型的。類型標註(annotation)是可選的(由於有類型推論)但強烈鼓勵。標註存在於定義之上的一行(不同於C家族語言,這裡的類型和名字是夾雜在一起的)。Elm使用單一的冒號表達「擁有類型」。
類型包括原始類型如整數和字符串,和基本數據結構比如列表、元組和記錄。函數擁有用箭頭寫成的類型,例如round : Float -> Int
。定製類型允許編程者建立定製類型,以匹配特定問題領域的方式來表示數據[20]。
類型可以推論出其他類型,例如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公共庫中獲得到[21]。所有的庫都採用語義版本號,這是編譯器和其他工具所強制的。就是說,移除一個函數或改變它的類型只能在主要發行中進行。
同HTML、CSS和JavaScript的互操作
Elm使用叫做端口的抽象來與JavaScript通信[22]。它允許值流入和流出Elm程序,確使了在Elm和JavaScript之間的通信。
Elm有一個叫做elm/html的庫,編程者可以用來在Elm內書寫HTML和CSS[23]。它使用虛擬DOM方式來使更新有效率[24]。
後端
Elm官方上不支持服務器端開發。核心開發團隊不將它作為主要目標考慮,並且偏好聚焦於在增進前端開發體驗上的開發。儘管如此,有一些獨立的計劃,它們嘗試探索將Elm用於後端的可能性。這些計劃主要系附於Elm版本0.18.0,因為更新的版本不支持「原生」代碼和其他可利用特徵。有兩個嘗試將Elm用在BEAM(Erlang虛擬機)之上。其中一個計劃直接在這個環境上執行Elm[25],而另一個計劃把它編譯成Elixir[26]。還有一個嘗試通過Node.js下部結構為Elm建立後端框架[27]。這些計劃都沒有準備好用於生產。
Elm架構
Elm架構是建造交互式web應用的模式。Elm應用本質上以這種方式來構造,但是其他項目可能發現這個概念很有用。
Elm程序總是分解成三個部份:
- 模型:這個應用的狀態,
- 視圖:一個把模型轉變成HTML的函數,
- 更新:一個基於消息更新模型的函數。
這些是Elm架構的核心。
例如,想象顯示一個數值和在按下時增加這個數值的一個按鈕的一個應用[28]。在這種情況下,所有我們需要存儲的是一個數值,所以我們的模型可以簡單的就是type alias Model = Int
。view
函數將用Html
庫來定義並顯示這個數值和按鈕。為了讓這個數值被更新,我們需要能夠向update
函數發送消息,這是通過定製類型比如type Msg = Increase
來完成的。 Increase
值被附着於在view
函數內定義的按鈕,使得在用戶點擊這個按鈕的時候,Increase
被傳遞到update
函數之上,它可以通過增加這個數值來更新這個模型。
在Elm架構中,發送消息至update
是改變狀態的唯一方式。在更加複雜的應用中,消息可以來自各種來源:用戶交互,模型初始化,來自update
的內部調用,訂閱的外部事件(窗口改變大小、系統時鐘、JavaScript互操作等等)和URL變更及請求。
限制
Elm不支持高種類多態[29],這是同為函數式的語言Haskell和PureScript所提供的,Elm還不支持創建類型類。
這意味着,例如Elm沒有跨越多種數據結構如List
和Set
的通用的map
函數。在Elm中,這種函數典型的要限定上它們的模塊名字來調用,例如調用List.map
和Set.map
。在Haskell或PureScript中,只有一個函數map
。自從2015年這就是在Czaplicki的粗略路線圖上的一個周知的特徵要求[30]。
另一個缺陷是在中到大型項目中有大量的樣板代碼,如《Elm in Action》作者在他們的單一頁面應用例子中所展示的那樣[31],具有幾乎同樣的片段被重複於更新、視圖、訂閱、路由解析和建造函數之中。
樣例代碼
下面的例子代碼通過注釋展示了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。
- 函數式響應式編程
引用
- ^ Czaplicki, Evan. My Thesis is Finally Complete! "Elm: Concurrent FRP for functional GUIs". Reddit. [2021-02-27]. (原始內容存檔於2021-04-13).
- ^ elm/compiler. GitHub. [2021-02-27]. (原始內容存檔於2019-03-21).
- ^ Prior Art - Redux. redux.js.org. [2021-02-27]. (原始內容存檔於2020-09-21).
- ^ Comparison with Other Frameworks — Vue.js. [2021-02-27]. (原始內容存檔於2021-05-03).
- ^ 存档副本. [2021-02-27]. (原始內容存檔於2020-12-18).
- ^ Elm home page. [2021-02-27]. (原始內容存檔於2021-05-07).
- ^ Czaplicki, Evan. Elm: Concurrent FRP for Functional GUIs (PDF). [2021-02-27]. (原始內容 (PDF)存檔於2021-04-16).
csmith111. Programming with Elm-Signals.
Czaplicki, Evan. A Farewell to FRP. elm. [14 July 2018]. (原始內容存檔於2019-05-31).When I started working on my thesis in 2011, I stumbled upon this academic subfield called Functional Reactive Programming (FRP). By stripping that approach down to its simplest form, I ended up with something way easier to learn than similar functional languages. Signals meant piles of difficult concepts just were not necessary in Elm. ……
As The Elm Architecture emerged, it became clear that you could do almost all your Elm programming without thinking about signals at all. ……
In the end, it was possible to remove signals because Elm has been moving towards an explicit emphasis on concurrency for quite some time now. ……Just like with my thesis, Concurrent FRP, the goal is to get the benefits of concurrency for free. ……
Note: Interested readers may find Lucid Synchrone interesting. Unfortunately for me, I had no idea my thesis had so much in common with synchronous programming languages at the time, but the connections are quite striking. I might argue that Elm was never about FRP. - ^ Try Elm. elm-lang.org. [2019-07-24]. (原始內容存檔於2017-05-21).
- ^ elm and prezi. elm-lang.org. [2021-02-27]. (原始內容存檔於2021-04-17).
- ^ new adventures for elm. elm-lang.org. [2021-02-27]. (原始內容存檔於2021-04-17).
- ^ elm/compiler. GitHub. [2021-02-27]. (原始內容存檔於2021-04-14).
- ^ repl. elm-lang.org. [2021-02-27]. (原始內容存檔於2019-11-27).
- ^ package manager. elm-lang.org. [2021-02-27]. (原始內容存檔於2020-09-18).
- ^ Home. elm-lang.org. [2021-02-27]. (原始內容存檔於2020-12-03).
- ^ Install. guide.elm-lang.org. [2021-02-27]. (原始內容存檔於2019-07-24).
- ^ community created libraries. [2021-02-27]. (原始內容存檔於2015-02-11).
- ^ Ellie. [2022-05-12]. (原始內容存檔於2021-01-22).
- ^ syntax. elm-lang.org. [2013-05-31]. (原始內容存檔於2016-03-13).
- ^ elm/core. package.elm-lang.org. [2021-02-28]. (原始內容存檔於2021-04-17).
- ^ Model The Problem. Elm. [4 May 2016]. (原始內容存檔於2020-11-11).
- ^ Elm Public Library. [2021-02-27]. (原始內容存檔於2021-05-19).
- ^ JavaScript interop. elm-lang.org. [2021-02-28]. (原始內容存檔於2021-05-21).
- ^ elm/html. package.elm-lang.org. [2021-02-28]. (原始內容存檔於2021-05-11).
- ^ Blazing Fast HTML. elm-lang.org. [2021-02-28]. (原始內容存檔於2020-09-18).
- ^ 存档副本. [2021-02-28]. (原始內容存檔於2020-09-08).
- ^ 存档副本. [2021-02-28]. (原始內容存檔於2021-01-04).
- ^ 存档副本. [2021-02-28]. (原始內容存檔於2020-11-22).
- ^ Buttons · An Introduction to Elm. guide.elm-lang.org. [2020-10-15]. (原始內容存檔於2020-11-11).
- ^ Higher-Kinded types Not Expressible? #396. github.com/elm-lang/elm-compiler. [6 March 2015].
- ^ Higher-Kinded types Not Expressible #396. github.com/elm-lang/elm-compiler. [19 November 2019]. (原始內容存檔於2020-12-18).
- ^ Main.elm. github.com/rtfeldman/elm-spa-example. [30 June 2020].
外部連結
- 官方網站
- Awesome Elm. [2021-03-02]. (原始內容存檔於2021-01-17).