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

Quick Facts 編程範型, 設計者 ...
Elm
Thumb
編程範型函數式
設計者Evan Czaplicki
面市時間2012年3月30日,​12年前​(2012-03-30[1]
目前版本
  • 0.19.1 (2019年10月21日)
編輯維基數據鏈結
型態系統靜態, 強型別, 類型推論
許可證寬鬆許可證 (三條款BSD許可證)[2]
副檔名.elm
網站elm-lang.org 編輯維基數據鏈結
啟發語言
Haskell, Standard ML, OCaml, F#
影響語言
Redux,[3] Vuex[4]
Close

歷史

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

Elm編譯器的最初實現執行目標為HTMLCSSJavaScript[11]。核心工具集持續的擴充,現在包括了REPL[12]包管理器[13]、時間旅行除錯器[14]和針對macOS及Windows的安裝器[15]。 Elm還有一個生態系統,包括社群建立的庫[16]和Ellie[17],它是一個進階線上編輯器,允許儲存工作和包含社群庫。

特徵

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

不可變性

在Elm中所有的值都是不可變的,這意味著一個值不能在建立之後修改。Elm使用永續性資料結構來實現它的ArrayDictSet[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,它包括IntFloat,用來利用數值算符比如(+)(*)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 = Intview函式將用Html庫來定義並顯示這個數值和按鈕。為了讓這個數值被更新,我們需要能夠向update函式傳送訊息,這是通過客製化類型比如type Msg = Increase來完成的。 Increase值被附著於在view函式內定義的按鈕,使得在使用者點擊這個按鈕的時候,Increase被傳遞到update函式之上,它可以通過增加這個數值來更新這個模型。

在Elm架構中,傳送訊息至update是改變狀態的唯一方式。在更加複雜的應用中,訊息可以來自各種來源:使用者互動,模型初始化,來自update的內部呼叫,訂閱的外部事件(窗口改變大小、系統時鐘、JavaScript互操作等等)和URL變更及請求。

限制

Elm不支援高種類多型[29],這是同為函數式的語言HaskellPureScript所提供的,Elm還不支援建立類型類

這意味著,例如Elm沒有跨越多種資料結構如ListSet的通用的map函式。在Elm中,這種函式典型的要限定上它們的模組名字來呼叫,例如呼叫List.mapSet.map。在Haskell或PureScript中,只有一個函式map。自從2015年這就是在Czaplicki的粗略路線圖上的一個周知的特徵要求[30]

另一個缺陷是在中到大型專案中有大量的樣板代碼英語Boilerplate code,如《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)

參見

參照

外部連結

Wikiwand in your browser!

Seamless Wikipedia browsing. On steroids.

Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.

Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.