Elm入门实践(二)——范例篇

记得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

个中TrueFalseAnonymosAuthed 都是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可以直接作为运用(想一想TrueFalse),而带有已知范例的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)

text << getAuthedUserName 运用了Elm中的<<操作符完成两个函数的compose,类似于lodash中的_.flowRight函数

Type variables

上面已提到了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的文章

    原文作者:kpaxqin
    原文地址: https://segmentfault.com/a/1190000005701589
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞