摘要:如果不聲明類型呢如果注釋掉類型注解重新編譯,還是會(huì)報(bào)錯(cuò),只是錯(cuò)誤信息變了,這次是第行即使沒有顯式的類型注解,的類型推導(dǎo)系統(tǒng)也會(huì)發(fā)揮作用,此處通過類型推導(dǎo)認(rèn)為函數(shù)的參數(shù)應(yīng)該是字符串,但是傳入了數(shù)字,因此報(bào)錯(cuò)。
記得Facebook曾經(jīng)在一次社區(qū)活動(dòng)上說過,隨著他們?cè)絹碓蕉嗟厥褂肑avascript,很快就面臨了曾經(jīng)在PHP上遇到的問題:這東西到底是啥?
動(dòng)態(tài)語言就像把雙刃劍,你可以愛死它的靈活性,也可能因?yàn)橐粋€(gè)小的疏忽而損失慘重。Elm選擇了靜態(tài)強(qiáng)類型,這通常也是多數(shù)函數(shù)式語言的選擇,沒有了OO語言中類的概念,強(qiáng)大的類型系統(tǒng)負(fù)責(zé)解決一切“這是什么?”的問題
類型注解也可以叫做類型簽名,Elm 使用冒號(hào):來注明類型,在Hello world的基礎(chǔ)上,讓我們分別定義一個(gè)變量和函數(shù),并且注明類型
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"改為數(shù)字,看看會(huì)發(fā)生什么?
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
編譯器發(fā)現(xiàn)了錯(cuò)誤,并且能夠定位到具體的行數(shù)。
如果不聲明類型呢?如果注釋掉類型注解
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)] ]
重新編譯,還是會(huì)報(bào)錯(cuò),只是錯(cuò)誤信息變了,這次是第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的類型推導(dǎo)系統(tǒng)也會(huì)發(fā)揮作用,此處通過類型推導(dǎo)認(rèn)為sayHello函數(shù)的參數(shù)應(yīng)該是字符串,但是傳入了數(shù)字,因此報(bào)錯(cuò)。
對(duì)比兩次不同的錯(cuò)誤提示可以看出,類型注解能讓編譯器更準(zhǔn)確地發(fā)現(xiàn)和定位錯(cuò)誤。隨著學(xué)習(xí)的深入你會(huì)慢慢喜歡上類型系統(tǒng)帶來的安全感:如果編譯失敗,明確的提示能幫助你快速定位問題。而只要編譯通過,程序就一定能運(yùn)行。
基本類型和List 基本類型基本類型和多數(shù)語言是類似的,無非就是String, Char, Bool Int, Float,可以參考官網(wǎng)的literals。需要注意在Elm中,String必須用雙引號(hào),單引號(hào)是用來表示Char的,字符串單引號(hào)黨需要適應(yīng)一下。
List嚴(yán)格來說List并不是類型,它的類型是List a,其中的a被稱作類型變量,這是因?yàn)長ist作為容器,它可以裝String,Int,或者什么都不裝,因此類型必須是動(dòng)態(tài)的:
> [ "Alice", "Bob" ] [ "Alice", "Bob" ] : List String > [ 1.0, 8.6, 42.1 ] [ 1.0, 8.6, 42.1 ] : List Float > [] [] : List a
關(guān)于類型變量后面會(huì)繼續(xù)討論,在這里我們只需要記住一點(diǎn):List不是類型,類似List String這樣的才是。
由于參數(shù)只有一個(gè),Elm的List只能容納單一類型的元素,和Javascript來者不拒的List不同,下面這樣的是會(huì)被編譯器發(fā)現(xiàn)并報(bào)錯(cuò)的:
list = [1, "a"]類型別名
類型別名用于組合或復(fù)用已知的類型,比如
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}
它不僅可以讓基本類型具備業(yè)務(wù)語義,還可以為復(fù)雜的數(shù)據(jù)結(jié)構(gòu)組合出合適的、語義化的類型。沒有別名的話,setUserName的類型簽名就得寫成下面這樣……一坨:
setUserName : String -> {name: String, age: Int} -> {name: String, age: Int}Union Types
Union type 是Elm類型系統(tǒng)中最重要的部分之一,它用來表示一組可能的值,每個(gè)值叫做一個(gè)Tag,如下:
type Bool = True | False type User = Anonymos | Authed
其中True和False, Anonymos和Authed 都是Tag名(注意Tag不是Type)。看起來很像枚舉?不只這樣,Union type強(qiáng)大的地方在于:Tag可以攜帶一組已知類型。上面的代碼我們雖然能區(qū)分兩類用戶,但并不能獲取認(rèn)證用戶的名稱,這時(shí)候就可以用已知類型結(jié)合Tag表達(dá):
type User = Anonymos | Authed String
當(dāng)我們創(chuàng)建Union type的時(shí)候,實(shí)際上為每個(gè)Tag都創(chuàng)建了相應(yīng)的值構(gòu)造器:
> type User = Anonymous | Authed String > Anonymous Anonymous : User > Authed Authed : String -> User
不帶其它信息的Anonymous可以直接作為值使用(想想True和False),而帶有已知類型的Authed實(shí)際上是一個(gè)函數(shù),它接受String,返回User類型:
users : List User users = [ Anonymous, Authed "Kpax"]
在Haskell中沒有Tag的叫法,相似的東西就叫值構(gòu)造器(value constructor),直接的表明了它的用途:構(gòu)建屬于該類型的值
Tag還可以被解構(gòu):
getAuthedUserName : User -> String getAuthedUserName user = case user of Anonymous -> "" Authed name -> name
這個(gè)函數(shù)返回Authed用戶的名稱,如果是Anonymous用戶則返回空字符串。
完整的可在在線編輯器中執(zhí)行的代碼如下:
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中的<<操作符實(shí)現(xiàn)兩個(gè)函數(shù)的compose,類似于lodash中的_.flowRight函數(shù)
上面已經(jīng)提到了List a類型,其中a即類型變量,表示一個(gè)當(dāng)前還不確定的類型,類似于面向?qū)ο缶幊讨蟹盒偷母拍?/p>
map函數(shù)的類型簽名也使用了類型變量:
map : (a -> b) -> a -> b
這使得我們調(diào)用map函數(shù)map userToString user時(shí),只要保證user是User類型即可,map函數(shù)并不關(guān)心具體的類型。
那么如何定義一個(gè)List a類型呢?代碼如下
type List a = Empty | Node a (List a)
前面說到Tag可以攜帶已知類型,那么是否可以攜帶正在定義的這個(gè)類型呢?答案是肯定的!這就是類型的遞歸,List a就是這樣一個(gè)帶有類型參數(shù)的遞歸類型,平時(shí)我們寫的數(shù)組,可以理解為如下代碼的語法糖
-- [] Empty -- [1] Node 1 Empty -- [1, 2, 3] Node 1 (Node 2 (Node 3 Empty))
同樣的思路,我們完全可以自己實(shí)現(xiàn)二叉樹等數(shù)據(jù)結(jié)構(gòu),有興趣的朋友不妨試試,官方文檔有相關(guān)章節(jié)可供參考
Counter with type上一章[基礎(chǔ)篇]()我們講了Counter的實(shí)現(xiàn),代碼如下:
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}
讓我們用剛剛學(xué)習(xí)的知識(shí)給以上代碼添加類型和類型注解
首先,我們有initModel這個(gè)數(shù)據(jù),它的類型是Int,不具備任何業(yè)務(wù)語義,讓我們定義一個(gè)類型別名Model來表示Counter的數(shù)據(jù)
type alias Model = Int
自然initModel的類型應(yīng)該為Model
initModel : Model initModel = 3
update函數(shù)的類型簽名比較簡單,它接受消息Msg和當(dāng)前數(shù)據(jù)Model,返回新的數(shù)據(jù)Model:
update : Msg -> Model -> Model
view函數(shù)接受Model類型的數(shù)據(jù),返回什么呢?如果查閱div函數(shù)的文檔,你會(huì)發(fā)現(xiàn)返回的是一個(gè)帶有類型變量的類型Html msg。其實(shí)很好理解,因?yàn)殇秩窘缑娴暮瘮?shù)不僅要輸出Html,當(dāng)事件發(fā)生時(shí)還要輸出消息,輸出消息的類型,就是應(yīng)該賦給變量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}總結(jié)
類型的學(xué)習(xí)可能有些枯燥,但是非常重要。如果你了解redux,你會(huì)發(fā)現(xiàn)Union type簡直天生就是做action的料,比起redux在javascript中使用的字符串既簡潔又達(dá)意,甚至還可以嵌套組合,談笑風(fēng)生!高到不知道哪里去了!
下一章我們將把在線編輯器放到一邊,把Counter遷移到本地運(yùn)行,然后實(shí)現(xiàn)一個(gè)CounterList,在CounterList中,你會(huì)看到Elm是如何復(fù)用組件,以及為什么Elm被稱為理想的分形架構(gòu)。
各種架構(gòu)對(duì)比,可以參考Cycle.js作者Andre Staltz的文章
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/79638.html
摘要:由于內(nèi)容較多,計(jì)劃分四篇,大致內(nèi)容分布如下基礎(chǔ)篇介紹基礎(chǔ)。接下來讓我們補(bǔ)全這一部分在第行我們引入了模塊中函數(shù),可以理解為當(dāng)事件發(fā)生時(shí),它會(huì)輸出一個(gè)消息。我們有了數(shù)據(jù),具備行為的視圖,按行為改變數(shù)據(jù)的邏輯,卻沒有將它們粘合成一個(gè)應(yīng)用。 簡介 Elm 是一門專注于Web前端的純函數(shù)式語言。你可能沒聽說過它,但一定聽說過Redux,而Redux的核心reducer就是受到了Elm的啟發(fā)。 隨...
摘要:函數(shù)式編程,一看這個(gè)詞,簡直就是學(xué)院派的典范。所以這期周刊,我們就重點(diǎn)引入的函數(shù)式編程,淺入淺出,一窺函數(shù)式編程的思想,可能讓你對(duì)編程語言的理解更加融會(huì)貫通一些。但從根本上來說,函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進(jìn)行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數(shù)式編程(Functional Programming),一...
摘要:我的目的是確保所有引用的使用都是絕對(duì)安全的,編譯器會(huì)自動(dòng)進(jìn)行檢查。它導(dǎo)致了數(shù)不清的錯(cuò)誤漏洞和系統(tǒng)崩潰,可能在之后年中造成了十億美元的損失。這個(gè)函數(shù)將使用一個(gè)表示我們希望進(jìn)行轉(zhuǎn)換的函數(shù)參數(shù),并返回一個(gè)包含轉(zhuǎn)換結(jié)果的新參數(shù)。 翻譯原文出處:Building a Maybe in JavaScript 鄙人翻譯略差且略有出入,別見笑。 很多時(shí)候我們會(huì)碰到:Uncaught TypeError...
摘要:值得一瞥的相關(guān)技術(shù)趨勢(shì)從屬于筆者的前端入門與工程實(shí)踐,推薦閱讀我的前端之路工具化與工程化獲得更多關(guān)于年前端總結(jié)。的不少開發(fā)者都是的粉絲,他們的以及都是基于構(gòu)建的。 2017值得一瞥的JavaScript相關(guān)技術(shù)趨勢(shì)從屬于筆者的Web 前端入門與工程實(shí)踐,推薦閱讀2016-我的前端之路:工具化與工程化獲得更多關(guān)于2016年前端總結(jié)。本文主要內(nèi)容翻譯自,筆者對(duì)于每個(gè)條目進(jìn)行了些許完善。本文...
閱讀 1439·2021-09-03 10:29
閱讀 3463·2019-08-29 16:24
閱讀 2023·2019-08-29 11:03
閱讀 1416·2019-08-26 13:52
閱讀 2930·2019-08-26 11:36
閱讀 2793·2019-08-23 17:19
閱讀 565·2019-08-23 17:14
閱讀 813·2019-08-23 13:59