摘要:如果不聲明類型呢如果注釋掉類型注解重新編譯,還是會報錯,只是錯誤信息變了,這次是第行即使沒有顯式的類型注解,的類型推導系統也會發揮作用,此處通過類型推導認為函數的參數應該是字符串,但是傳入了數字,因此報錯。
記得Facebook曾經在一次社區活動上說過,隨著他們越來越多地使用Javascript,很快就面臨了曾經在PHP上遇到的問題:這東西到底是啥?
動態語言就像把雙刃劍,你可以愛死它的靈活性,也可能因為一個小的疏忽而損失慘重。Elm選擇了靜態強類型,這通常也是多數函數式語言的選擇,沒有了OO語言中類的概念,強大的類型系統負責解決一切“這是什么?”的問題
類型注解也可以叫做類型簽名,Elm 使用冒號:來注明類型,在Hello world的基礎上,讓我們分別定義一個變量和函數,并且注明類型
import Html exposing (..) import Html.Attributes exposing (..) elm : String elm = "elm" sayHello : String -> String sayHello name = "Hello, " ++ name main = div [class "hello"] [ span [] [text (sayHello elm)] ]
嘗試將elm的值"elm"改為數字,看看會發生什么?
Detected errors in 1 module. -- TYPE MISMATCH --------------------------------------------------------------- The type annotation for `elm` does not match its definition. 5| elm : String ^^^^^^ The type annotation is saying: String But I am inferring that the definition has this type: number
編譯器發現了錯誤,并且能夠定位到具體的行數。
如果不聲明類型呢?如果注釋掉類型注解
import Html exposing (..) import Html.Attributes exposing (..) --elm : String elm = 6 --sayHello : String -> String sayHello name = "Hello, " ++ name main = div [class "hello"] [ span [] [text (sayHello elm)] ]
重新編譯,還是會報錯,只是錯誤信息變了,這次是第14行:
Detected errors in 1 module. -- TYPE MISMATCH --------------------------------------------------------------- The argument to function `sayHello` is causing a mismatch. 14| sayHello elm) ^^^ Function `sayHello` is expecting the argument to be: String But it is: number
即使沒有顯式的類型注解,Elm的類型推導系統也會發揮作用,此處通過類型推導認為sayHello函數的參數應該是字符串,但是傳入了數字,因此報錯。
對比兩次不同的錯誤提示可以看出,類型注解能讓編譯器更準確地發現和定位錯誤。隨著學習的深入你會慢慢喜歡上類型系統帶來的安全感:如果編譯失敗,明確的提示能幫助你快速定位問題。而只要編譯通過,程序就一定能運行。
基本類型和List 基本類型基本類型和多數語言是類似的,無非就是String, Char, Bool Int, Float,可以參考官網的literals。需要注意在Elm中,String必須用雙引號,單引號是用來表示Char的,字符串單引號黨需要適應一下。
List嚴格來說List并不是類型,它的類型是List a,其中的a被稱作類型變量,這是因為List作為容器,它可以裝String,Int,或者什么都不裝,因此類型必須是動態的:
> [ "Alice", "Bob" ] [ "Alice", "Bob" ] : List String > [ 1.0, 8.6, 42.1 ] [ 1.0, 8.6, 42.1 ] : List Float > [] [] : List a
關于類型變量后面會繼續討論,在這里我們只需要記住一點:List不是類型,類似List String這樣的才是。
由于參數只有一個,Elm的List只能容納單一類型的元素,和Javascript來者不拒的List不同,下面這樣的是會被編譯器發現并報錯的:
list = [1, "a"]類型別名
類型別名用于組合或復用已知的類型,比如
type alias Name = String type alias Age = Int type alias User = {name: Name, age: Age} user : User user = { name = "Zhang zhe", age = 89 } setUserName : String -> User -> User setUserName name user = {user | name = name}
它不僅可以讓基本類型具備業務語義,還可以為復雜的數據結構組合出合適的、語義化的類型。沒有別名的話,setUserName的類型簽名就得寫成下面這樣……一坨:
setUserName : String -> {name: String, age: Int} -> {name: String, age: Int}Union Types
Union type 是Elm類型系統中最重要的部分之一,它用來表示一組可能的值,每個值叫做一個Tag,如下:
type Bool = True | False type User = Anonymos | Authed
其中True和False, Anonymos和Authed 都是Tag名(注意Tag不是Type)。看起來很像枚舉?不只這樣,Union type強大的地方在于:Tag可以攜帶一組已知類型。上面的代碼我們雖然能區分兩類用戶,但并不能獲取認證用戶的名稱,這時候就可以用已知類型結合Tag表達:
type User = Anonymos | Authed String
當我們創建Union type的時候,實際上為每個Tag都創建了相應的值構造器:
> type User = Anonymous | Authed String > Anonymous Anonymous : User > Authed Authed : String -> User
不帶其它信息的Anonymous可以直接作為值使用(想想True和False),而帶有已知類型的Authed實際上是一個函數,它接受String,返回User類型:
users : List User users = [ Anonymous, Authed "Kpax"]
在Haskell中沒有Tag的叫法,相似的東西就叫值構造器(value constructor),直接的表明了它的用途:構建屬于該類型的值
Tag還可以被解構:
getAuthedUserName : User -> String getAuthedUserName user = case user of Anonymous -> "" Authed name -> name
這個函數返回Authed用戶的名稱,如果是Anonymous用戶則返回空字符串。
完整的可在在線編輯器中執行的代碼如下:
import Html exposing (..) import List type User = Anonymous | Authed String users : List User users = [ Anonymous, Authed "Kpax", Authed "qin"] getAuthedUserName : User -> String getAuthedUserName user = case user of Anonymous -> "" Authed name -> name main = div [] (List.map (text << getAuthedUserName) users)
Type variablestext << getAuthedUserName 使用了Elm中的<<操作符實現兩個函數的compose,類似于lodash中的_.flowRight函數
上面已經提到了List a類型,其中a即類型變量,表示一個當前還不確定的類型,類似于面向對象編程中泛型的概念
map函數的類型簽名也使用了類型變量:
map : (a -> b) -> a -> b
這使得我們調用map函數map userToString user時,只要保證user是User類型即可,map函數并不關心具體的類型。
那么如何定義一個List a類型呢?代碼如下
type List a = Empty | Node a (List a)
前面說到Tag可以攜帶已知類型,那么是否可以攜帶正在定義的這個類型呢?答案是肯定的!這就是類型的遞歸,List a就是這樣一個帶有類型參數的遞歸類型,平時我們寫的數組,可以理解為如下代碼的語法糖
-- [] Empty -- [1] Node 1 Empty -- [1, 2, 3] Node 1 (Node 2 (Node 3 Empty))
同樣的思路,我們完全可以自己實現二叉樹等數據結構,有興趣的朋友不妨試試,官方文檔有相關章節可供參考
Counter with type上一章[基礎篇]()我們講了Counter的實現,代碼如下:
import Html exposing (..) import Html.Events exposing (onClick) import Html.App as App type Msg = Increment | Decrement update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 view model = div [] [ button [onClick Decrement] [text "-"] , text (toString model) , button [onClick Increment] [text "+"] ] initModel = 3 main = App.beginnerProgram {model = initModel, view = view, update = update}
讓我們用剛剛學習的知識給以上代碼添加類型和類型注解
首先,我們有initModel這個數據,它的類型是Int,不具備任何業務語義,讓我們定義一個類型別名Model來表示Counter的數據
type alias Model = Int
自然initModel的類型應該為Model
initModel : Model initModel = 3
update函數的類型簽名比較簡單,它接受消息Msg和當前數據Model,返回新的數據Model:
update : Msg -> Model -> Model
view函數接受Model類型的數據,返回什么呢?如果查閱div函數的文檔,你會發現返回的是一個帶有類型變量的類型Html msg。其實很好理解,因為渲染界面的函數不僅要輸出Html,當事件發生時還要輸出消息,輸出消息的類型,就是應該賦給變量msg的類型,在Counter中消息的類型是Msg,因此:
view : Model -> Html Msg
完整代碼:
import Html exposing (..) import Html.Events exposing (onClick) import Html.App as App type alias Model = Int type Msg = Increment | Decrement update : Msg -> Model -> Model update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 view : Model -> Html Msg view model = div [] [ button [onClick Decrement] [text "-"] , text (toString model) , button [onClick Increment] [text "+"] ] initModel : Model initModel = 3 main = App.beginnerProgram {model = initModel, view = view, update = update}總結
類型的學習可能有些枯燥,但是非常重要。如果你了解redux,你會發現Union type簡直天生就是做action的料,比起redux在javascript中使用的字符串既簡潔又達意,甚至還可以嵌套組合,談笑風生!高到不知道哪里去了!
下一章我們將把在線編輯器放到一邊,把Counter遷移到本地運行,然后實現一個CounterList,在CounterList中,你會看到Elm是如何復用組件,以及為什么Elm被稱為理想的分形架構。
各種架構對比,可以參考Cycle.js作者Andre Staltz的文章
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79638.html
摘要:由于內容較多,計劃分四篇,大致內容分布如下基礎篇介紹基礎。接下來讓我們補全這一部分在第行我們引入了模塊中函數,可以理解為當事件發生時,它會輸出一個消息。我們有了數據,具備行為的視圖,按行為改變數據的邏輯,卻沒有將它們粘合成一個應用。 簡介 Elm 是一門專注于Web前端的純函數式語言。你可能沒聽說過它,但一定聽說過Redux,而Redux的核心reducer就是受到了Elm的啟發。 隨...
摘要:函數式編程,一看這個詞,簡直就是學院派的典范。所以這期周刊,我們就重點引入的函數式編程,淺入淺出,一窺函數式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數式編程就是關于如使用通用的可復用函數進行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數式編程(Functional Programming),一...
摘要:我的目的是確保所有引用的使用都是絕對安全的,編譯器會自動進行檢查。它導致了數不清的錯誤漏洞和系統崩潰,可能在之后年中造成了十億美元的損失。這個函數將使用一個表示我們希望進行轉換的函數參數,并返回一個包含轉換結果的新參數。 翻譯原文出處:Building a Maybe in JavaScript 鄙人翻譯略差且略有出入,別見笑。 很多時候我們會碰到:Uncaught TypeError...
摘要:值得一瞥的相關技術趨勢從屬于筆者的前端入門與工程實踐,推薦閱讀我的前端之路工具化與工程化獲得更多關于年前端總結。的不少開發者都是的粉絲,他們的以及都是基于構建的。 2017值得一瞥的JavaScript相關技術趨勢從屬于筆者的Web 前端入門與工程實踐,推薦閱讀2016-我的前端之路:工具化與工程化獲得更多關于2016年前端總結。本文主要內容翻譯自,筆者對于每個條目進行了些許完善。本文...
閱讀 1441·2021-09-03 10:29
閱讀 3465·2019-08-29 16:24
閱讀 2028·2019-08-29 11:03
閱讀 1419·2019-08-26 13:52
閱讀 2932·2019-08-26 11:36
閱讀 2796·2019-08-23 17:19
閱讀 567·2019-08-23 17:14
閱讀 816·2019-08-23 13:59