Elm 快速指南



Elm - 简介

Elm 是一种函数式编程语言。它由 Evan Czaplicki 于 2012 年设计。

Elm 特别用于设计 Web 应用程序的前端。

Elm 编译成 JavaScript 并在浏览器中运行。它速度快、可测试、可维护,并且没有运行时异常。

Elm 编程平台的一些实际应用包括:

  • 游戏
  • 图形
  • 单页面应用程序

为什么选择 Elm

Elm 消除了前端开发人员面临的大多数常见问题。这包括:

没有运行时异常

Elm 是一种静态类型语言。所有可能的错误都在编译时进行验证和更正。这使得它能够没有运行时异常。

开发人员友好的错误消息

与其他编程语言不同,Elm 的编译器旨在在编译时提供非常具体且开发人员友好的错误消息。错误消息还包括提示,例如指向推荐的设计文档的链接。

易于测试

每个 Elm 函数都可以独立于所有其他函数进行测试。这使得用 Elm 编写的程序易于测试。

自动语义版本控制

Elm 强制对包进行自动语义版本控制。这确保补丁更改不会使正在运行的应用程序崩溃。

可重用代码

与 JavaScript、Python 或 TypeScript 中的函数相比,Elm 函数本质上易于重用。

Elm - 环境设置

本章讨论在 Windows、Mac 和 Linux 平台上安装 Elm 的步骤。

本地环境设置

请考虑以下步骤在您的本地环境中安装 Elm。

步骤 1 - 安装 node

由于 elm 编译成 JavaScript,目标机器应安装 node。有关设置 nodenpm 的步骤,请参阅 TutorialsPoint NodeJS 课程

Node 设置。

步骤 2 - 安装 elm

在终端上执行以下命令以安装 elm。请注意,在撰写本课程时,elm 的稳定版本为 0.18。

npm install -g [email protected]
Install elm

安装后,执行以下命令以验证 Elm 的版本。

C:\Users\dell>elm --version
0.18.0

步骤 2 - 安装编辑器

此处使用的开发环境是 Visual Studio Code(Windows 平台)。

Visual Studio Code 是来自 Visual Studio 的开源 IDE。它适用于 Mac OS X、Linux 和 Windows 平台。VSCode 可在以下网址获取

https://vscode.js.cn/。

在 Windows 上安装

在本节中,我们将讨论在 Windows 上安装 Elm 的步骤。

下载 https://vscode.js.cn/。 用于 Windows。

双击 VSCodeSetup.exe 启动安装过程。这只需要一分钟。

VSCodeSetup

您可以通过右键单击文件 → 在命令提示符中打开直接跳转到文件的路径。同样,“在资源管理器中显示”选项会在文件资源管理器中显示文件。

Reveal Explorer

在 Mac OS X 上安装

Visual Studio Code 的 Mac OS X 特定安装指南可在 VSCode 安装-MAC 找到。

在 Linux 上安装

Visual Studio Code 的 Linux 特定安装指南可在 VSCode 安装-Linux 找到。

步骤 4 - 安装 elm 扩展

如下所示在 VSCode 中安装 elm 扩展。

Installation Linux

Elm REPL

REPL 代表读取-求值-打印-循环。它表示一个计算机环境,例如 Windows 控制台或 Unix/Linux shell,用户在其中输入命令,系统以交互模式响应输出。

Elm 带有一个 REPL 环境。它执行以下任务:

  • 读取 - 读取用户的输入,将输入解析为 elm 数据结构,并存储在内存中。

  • 求值 - 获取并评估数据结构。

  • 打印 - 打印结果。

  • 循环 - 循环执行上述命令,直到用户退出。使用命令 :exit 退出 REPL 并返回到终端。

下面是一个在 REPL 中添加两个数字的简单示例:

打开 VSCode 终端并键入命令 elm REPL。

REPL 终端等待用户输入一些内容。输入以下表达式 10 + 20。REPL 环境处理输入如下所示:

  • 从用户处读取数字 10 和 20。

  • 使用 + 运算符进行求值。

  • 将结果打印为 30。

  • 循环等待下一个用户输入。在这里我们退出循环。

Elm REPL

Elm - 基本语法

本章讨论如何在 elm 中编写一个简单的程序。

步骤 1 - 在 VSCode 中创建一个目录 HelloApp

现在,在此目录中创建一个文件 - Hello.elm

Create directory

上图显示了项目文件夹 HelloApp 和在 VSCode 中打开的终端。

步骤 2 - 安装必要的 elm 包

elm 中的包管理器是 elm-package。安装 elm-lang/html 包。此包将帮助我们在浏览器中显示 elm 代码的输出。

通过右键单击文件 → 在 VSCode 中打开命令提示符来跳转到 HelloApp 项目文件夹。

在终端窗口中执行以下命令:

C:\Users\dell\Elm\HelloApp> elm-package install elm-lang/html

安装包后,以下文件/文件夹将添加到项目目录中。

  • elm-package.json(文件),存储项目元数据
  • elm-stuff(文件夹),存储外部包

包成功安装后,将显示以下消息。

package installed

步骤 3 - 将以下代码添加到 Hello.elm 文件中

-- importing Html module and the function text
import Html exposing (text)

-- create main method
main =
-- invoke text function
text "Hello Elm from TutorialsPoint"

上述程序将在浏览器中显示字符串消息 Hello Elm from TutorialsPoint

为此,我们需要在 Html 模块中导入函数 text。text 函数用于在浏览器中打印任何字符串值。main 方法是程序的入口点。main 方法调用 text 函数并将字符串值传递给它。

步骤 4 - 编译项目

在 VSCode 终端窗口中执行以下命令。

elm make Hello.elm

上述命令的输出如下所示:

//update path to the proj folder in the command elm make
C:\Users\dell\elm\HelloApp>elm make Hello.elm
Success! Compiled 38 modules.
Successfully generated index.html

上述命令将生成一个 index.html 文件。elm 编译器将 .elm 文件转换为 JavaScript 并将其嵌入到 index.html 文件中。

步骤 5 - 在浏览器中打开 index.html

在任何浏览器中打开 index.html 文件。输出将如下所示:

Open browser

Elm 中的注释

注释是提高程序可读性的一种方法。注释可用于包含有关程序的其他信息,例如代码作者、有关函数构造的提示等。注释被编译器忽略。

Elm 支持以下类型的注释:

  • 单行注释 (--) - -- 和行尾之间的任何文本都被视为注释。

  • 多行注释 ({- -}) - 这些注释可以跨越多行。

图示

-- this is single line comment

{- This is a
   Multi-line comment
-}

行和缩进

Elm 没有提供大括号来指示函数定义或流程控制的代码块。代码块由行缩进表示,并且严格执行。块中的所有语句必须缩进相同的量。例如:

module ModuleIf exposing (..)
x = 0

function1 =
   if x > 5 then
      "x is greater"
   else
      "x is small"

但是,以下块会生成错误:

-- Create file ModuleIf.elm
module ModuleIf exposing (..)
x = 0

function1 =
   if x > 5 then
      "x is greater"
         else --Error:else indentation not at same level of if statement
      "x is small"

因此,在 Elm 中,所有以相同空格数缩进的连续行将构成一个块。

C:\Users\admin>elm repl
---- elm-repl 0.18.0 -----------------------------------------------------------
   :help for help, :exit to exit, more at 
   <https://github.com/elm-lang/elm-repl>
   ---------------------------------------
   -----------------------------------------

> import ModuleIf exposing(..) -- importing module from ModuleIf.elm file
>function1 -- executing function from module
-- SYNTAX PROBLEM ---------------------------------------------------

I need whitespace, but got stuck on what looks like a new declaration. 
You are either missing some stuff in the declaration above or just need to add some spaces here:
7| else
   ^
I am looking for one of the following things:

   whitespace

Elm - 数据类型

类型系统表示语言支持的不同类型的值。类型系统在程序存储或操作提供的值之前检查其有效性。这确保代码按预期运行。类型系统还允许更丰富的代码提示和自动文档。

Elm 是一种静态类型语言。Elm 的类型类似于其他语言中的类型。

数字

number 数据类型表示数值。Elm 类型系统支持以下数值类型:

序号 类型 示例
1 number - 存储任何数字 7 是 number 类型
2 Float - 存储小数值 7/2 得到 3.5 结果为 Float
3 Int - 存储非小数值 7//2 得到 3 结果为 Int

number 类型可容纳小数和非小数值。打开 elm REPL 并尝试以下示例:

C:\Users\admin>elm repl
---- elm-repl 0.18.0 
---------------------------------------------
--------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
------------------------------------------
--------------------------------------
> 7
7 : number
> 7/2
3.5 : Float
> 7//2
3 : Int
>

字符串和字符

String 数据类型用于表示字符序列。Char 数据类型用于表示单个字符。String 值定义在双引号 " 内,Char 值包含在单引号 ' 内。

序号 类型 示例
1 String - 存储字符序列 "TutorialsPoint"
2 Char - 存储小数值 'T'

打开 elm REPL 并尝试以下示例:

C:\Users\admin>elm repl
---- elm-repl 0.18.0 ---------------------------------------
--------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------
------------------------------------------
> "TutorialsPoint"
"TutorialsPoint" : String
> 'T'
'T' : Char

布尔值

Elm 中的 Bool 数据类型仅支持两个值 - True 和 False。关键字 Bool 用于表示布尔值。

序号 类型 示例
1 Bool - 存储值 True 或 False 1==1 返回 True

打开 elm REPL 并尝试以下示例:

C:\Users\dell\elm>elm repl
---- elm-repl 0.18.0 -----------------------------------
------------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
----------------------------------------
----------------------------------------
> True
True : Bool
> False
False : Bool
> 1==1
True : Bool
> 1==2
False : Bool
> 1 /= 2 -- not equal
True : Bool
> not True
False : Bool
> not False
True : Bool

自定义类型

Elm 支持创建用户定义类型。例如,考虑一个支付应用程序。该应用程序需要存储不同的支付方式 - 信用卡、借记卡和网上银行。这可以通过定义自定义类型并将其值限制为三种可接受的支付方式来实现。

以下示例显示了如何创建自定义类型。

> type PaymentMode = CreditCard|NetBanking|DebitCard
> payment1 = CreditCard
CreditCard : Repl.PaymentMode
> payment2 = DebitCard
DebitCard : Repl.PaymentMode
> payment3 = UPI
-- NAMING ERROR ---------------------------------------------- repl-temp-000.elm

Cannot find variable `UPI`

7| payment3 = UPI

在上面的示例中,我们创建了一个 PaymentMode 自定义类型。变量 payment1 和 payment2 分配给 PaymentMode 值。如果分配给变量的值与 PaymentMode 类型定义的任何值都不匹配,则应用程序将抛出语法错误。

结构化数据类型

结构化数据类型可用于以结构化格式存储多个值。Elm 支持以下结构化数据类型:

  • 元组
  • 列表
  • 记录
  • 记录

这些将在后续章节中详细讨论。

Elm - 变量

根据定义,变量是“内存中命名的空间”,用于存储值。换句话说,它充当程序中值的容器。变量帮助程序存储和操作值。

Elm 中的变量与特定数据类型相关联。数据类型确定变量内存的大小和布局、可以存储在该内存中的值的范围以及可以对变量执行的操作集。

变量命名规则

在本节中,我们将了解变量命名规则。

  • 变量名可以由字母、数字和下划线字符组成。
  • 变量名不能以数字开头。它必须以字母或下划线开头。
  • 大小写字母是不同的,因为 Elm 区分大小写。

在 Elm 中声明变量

在 Elm 中声明变量的类型语法如下所示:

语法 1

variable_name:data_type = value

“ : ” 语法(称为类型注释)用于将变量与数据类型关联。

语法 2

variable_name = value-- no type specified

在 Elm 中声明变量时,数据类型是可选的。在这种情况下,变量的数据类型是从分配给它的值推断出来的。

图示

此示例使用 VSCode 编辑器编写 Elm 程序,并使用 Elm REPL 执行它。

步骤 1 - 创建一个项目文件夹 - VariablesApp。在项目文件夹中创建一个 Variables.elm 文件。

将以下内容添加到文件中。

module Variables exposing (..) //Define a module and expose all contents in the module
message:String -- type annotation
message = "Variables can have types in Elm"

程序定义了一个名为 Variables 的模块。模块的名称必须与 Elm 程序文件相同。(..) 语法用于公开模块中的所有组件。

程序声明了一个类型为 String 的变量 message。

Illustration

步骤 2 - 执行程序。

  • 在 VSCode 终端中键入以下命令以打开 Elm REPL。
elm repl
  • 在 REPL 终端中执行以下 Elm 语句。
> import Variables exposing (..) --imports all components from the Variables module
> message --Reads value in the message varaible and prints it to the REPL 
"Variables can have types in Elm":String
>

图示

使用 Elm REPL 尝试以下示例。

C:\Users\dell\elm>elm repl
---- elm-repl 0.18.0 ---------------------------------------
--------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
-------------------------------------
------------------------------------------
> company = "TutorialsPoint"
"TutorialsPoint" : String
> location = "Hyderabad"
"Hyderabad" : String
> rating = 4.5
4.5 : Float

这里,变量 company 和 location 是 String 变量,rating 是 Float 变量。

Elm REPL 不支持变量的类型注释。如果在声明变量时包含数据类型,则以下示例将引发错误。

C:\Users\dell\elm>elm repl
---- elm-repl 0.18.0 -----------------------------------------
------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
----------------------------------------
----------------------------------------
> message:String
-- SYNTAX PROBLEM -------------------------------------------- repl-temp-000.elm

A single colon is for type annotations. Maybe you want :: instead? Or maybe you
are defining a type annotation, but there is whitespace before it?

3| message:String
^

Maybe <http://elm-lang.org/docs/syntax> can help you figure it out.

要在使用 Elm REPL 时插入换行符,请使用 \ 语法,如下所示 -

C:\Users\dell\elm>elm repl
---- elm-repl 0.18.0 --------------------------------------
---------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
------------------------------------------
--------------------------------------
> company \ -- firstLine
| = "TutorialsPoint" -- secondLine
"TutorialsPoint" : String

Elm - 运算符

运算符定义了一些将在数据上执行的函数。运算符作用的值称为操作数。考虑以下表达式

7 + 5 = 12

这里,值 7、5 和 12 是操作数,而 + 和 = 是运算符。

Elm 中的主要运算符可以分类为 -

  • 算术运算符
  • 关系运算符
  • 逻辑运算符

算术运算符

假设变量 a 和 b 中的值分别为 7 和 2。

显示示例

序号 运算符 描述 示例
1 +(加法) 返回操作数的和 a+b 为 9
2 -(减法) 返回值的差 a-b 为 5
3 *(乘法) 返回值的积 a*b 为 14
4 /(浮点数除法) 执行除法运算并返回浮点数商 a / b 为 3.5
5 //(整数除法) 执行除法运算并返回整数商 a // b 为 3
6 %(模) 执行除法运算并返回余数 a % b 为 1

关系运算符

关系运算符测试或定义两个实体之间关系的种类。这些运算符用于比较两个或多个值。关系运算符返回布尔值,即 true 或 false。

假设 a 的值为 10,b 的值为 20。

显示示例

序号 运算符 描述 示例
1 > 大于 (a > b) 为 False
2 < 小于 (a < b) 为 True
3 >= 大于或等于 (a >= b) 为 False
4 <= 小于或等于 (a <= b) 为 True
5 == 相等 (a == b) 为 false
6 != 不相等 (a != b) 为 True

可比较类型

比较运算符(如 >= 或 <)适用于可比较类型。这些定义为数字、字符、字符串和列表、元组。运算符两侧的可比较类型必须相同。

序号 可比较类型 示例
1 数字 7>2 返回 True
2 字符 'a' =='b' 返回 False
3 字符串 "hello" =="hello" 返回 True
4 元组 (1,"One")==(1,"One") 返回 True
5 列表 [1,2]==[1,2] 返回 True

打开 Elm REPL 并尝试以下示例 -

C:\Users\admin>elm repl
---- elm-repl 0.18.0 -----------------------------------------------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
> 7>2
True : Bool
> 7.0>2
True : Bool
> 7.0<2.0
False : Bool
> 'a' > 'b'
False : Bool
> 'a' < 'b'
True : Bool
> "a" < "b"
True : Bool
> (1,2) > (2,3)
False : Bool
> ['1','3'] < ['2','1']
True : Bool
>

逻辑运算符

逻辑运算符用于组合两个或多个条件。逻辑运算符也返回布尔值。

显示示例

序号 运算符 描述 示例
1 && 只有当指定的所有表达式都返回 true 时,运算符才返回 true (10>5) && (20>5) 返回 True
2 || 如果指定表达式中至少有一个返回 true,则运算符返回 true (10 < 5) || (20 >5) 返回 True
3 运算符返回表达式的结果的反值。例如:!(>5) 返回 false。 not (10 < 5) 返回 True
4 异或 只有当且仅当一个输入返回 true 时,运算符才返回 true。如果两个表达式都返回 true,则运算符返回 false。 xor (10 > 5 ) (20 > 5) 返回 false

Elm - 决策

决策结构要求程序员指定一个或多个要由程序评估或测试的条件,以及如果条件确定为 true 则要执行的语句或语句,以及可选地,如果条件确定为 false 则要执行的其他语句。

下面显示了大多数编程语言中常见的典型决策结构的通用形式

Decision-making

决策结构在执行指令之前评估条件。Elm 中的决策结构分类如下 -

序号 语句 描述
1 if...then...else 语句 if 语句由一个布尔表达式后跟 then 组成,如果表达式返回 true 则执行,否则执行 else,如果表达式返回 false 则执行。
2 嵌套 if 语句 您可以在另一个 if 内部使用一个 if...then...else。
3 case 语句 将变量的值与值列表进行比较。

if...then...else 语句

if…then 结构在执行代码块之前评估条件。如果布尔表达式计算结果为 true,则将执行 then 语句内部的代码块。如果布尔表达式计算结果为 false,则将执行 else 语句内部的代码块。

与其他编程语言不同,在 Elm 中我们必须提供 else 分支。否则,Elm 将抛出错误。

语法

if boolean_expression then statement1_ifTrue else statement2_ifFalse

图示

在 REPL 终端中尝试以下示例。

> if 10>5 then "10 is bigger" else "10 is small"
"10 is bigger" : String

嵌套 If

嵌套 if 语句可用于测试多个条件。嵌套 if 语句的语法如下所示 -

if boolean_expression1 then statement1_ifTrue else if boolean_expression2 then statement2_ifTrue else statement3_ifFalse

图示

在 Elm REPL 中尝试以下示例 -

> score=80
80 : number
> if score>=80 then "Outstanding" else if score > = 70 then "good" else "average"
"Outstanding" : String

Case 语句

case 语句可用于简化 if then else 语句。case 语句的语法如下所示 -

case variable_name of
   constant1 -> Return_some_value
   constant2 -> Return_some_value
   _ -> Return_some_value if none of the above values match

case 语句检查变量的值是否与预定义的一组常量匹配,并返回相应的值。请注意,每个 case 返回的值必须是相同类型。如果变量的值与任何给定的常量都不匹配,则控制权将传递给 * default *(用 //_ 表示)并返回相应的值。

图示

在 Elm REPL 中尝试以下示例 -

> n = 10
10 : number
> case n of \
| 0 -> "n is Zero" \
| _ -> "n is not Zero"
"n is not Zero" : String

上面的代码片段检查 n 的值是否为零。控制权传递给 default,它返回字符串“n is not Zero”。

Elm - 循环

Elm 是一种函数式编程语言。Elm 使用递归的概念作为传统循环结构的替代方案。

本章讨论递归的概念。

递归

某些计算机编程语言允许模块或函数调用自身。此技术称为递归。

图示

在此程序中,我们将了解如何使用递归将 hello 显示五次。

步骤 1 - 创建一个文件 Loop.elm

创建一个名为 Loop 的模块并定义一个函数 sayHello。函数 sayHello 以整数作为输入并返回字符串值。

module Loop exposing(..)
//function signature
sayHello:Int ->String
//function implementation
sayHello n =
   case n of
   1 -> "Hello:1 "
   _ -> "Hello:" ++ toString (n) ++ " " ++ sayHello(n-1)

函数 sayHello 检查传递的参数是否为 1。如果参数为 1,则函数将返回,否则它将创建一个字符串 Hello 并调用同一个函数。

步骤 2 - 从 REPL 调用 sayHello

从当前项目文件夹(Loop.elm 文件的位置)打开 Elm REPL。

//import the module Loop
> import Loop exposing(..)
//invoke the sayHello function with parameter value as 5
> sayHello 5
"Hello:5 Hello:4 Hello:3 Hello:2 Hello:1 Hello:0 " : String
>
module Loop

图示

以下示例使用递归打印 n 个数字的总和。

> sumOfNos n =\
| if n==0 then 0 \
| else (n) + sumOfNos (n-1)
<function> : number -> number1

在 Elm REPL 中,我们创建了一个名为 sumOfNos 的函数,它接受一个输入数字并将 0 到该数字的所有数字求和。

例如,如果我们传递输入为 5,它将计算 1+2+3+4+5 的和,结果为 15

> ssumOfNos 5
15 : number

程序的输出如上所示。

Elm - 函数

函数是 Elm 程序的基本构建块。函数是一组执行特定任务的语句。

函数将程序组织成逻辑代码块。定义后,可以调用函数来访问代码。这使得代码可重用。此外,函数使程序代码易于阅读和维护。

使用函数的步骤

使用函数有三个步骤 -

函数声明

函数声明告诉编译器函数的名称、返回类型和参数。声明函数的语法如下所示 -

fn_name:data_type_of_the_parameters ->return_type

函数声明指定以下内容 -

  • 函数的名称。

  • 参数的数据类型。这是可选的,因为函数可能有也可能没有参数。

  • 函数将返回的值的数据类型。Elm 中的函数必须始终返回值,因为 Elm 是一种函数式编程语言。与其他编程语言中的函数不同,Elm 函数不使用 return 关键字返回值。

函数定义或函数实现

函数定义提供了函数的实际主体。函数定义指定如何完成特定任务。定义函数的语法如下所示 -

fn_name parameter1 parameter2 = statements

调用或调用函数

必须调用函数才能执行它。调用函数的语法如下所示 -

fn_name parameter1 parameter2

图示

以下代码定义了一个名为 greet 的函数。该函数返回字符串“Hello”。

> greet = \
| if True then \
| "Hello" \
| else \
| "GoodBye"
"Hello" : String
> greet
"Hello" : String

参数化函数

参数是将值传递给函数的一种机制。参数的值在函数调用时传递给函数。

插图 1

以下示例定义了一个名为 fn_add 的函数。该函数接受两个数字作为参数并返回它们的和。在 Elm REPL 中尝试以下操作 -

> fn_add x y = x+y
<function> : number -> number -> number
> fn_add 10 20
30 : number

插图 2

以下示例定义了一个名为 sayHello 的函数。sayHello 函数接受并返回一个 String 值作为参数并返回一个 String。

> sayHello name = "Hello "++ name
<function> : String -> String
> sayHello "Tutorialspoint"
"Hello Tutorialspoint" : String
>

管道运算符

要理解管道运算符 |>,让我们考虑一个示例,其中我们有一个包含不同字符串 ["a","b","c"] 的列表。现在我们需要一个用 - 分隔的单个字符串

以下示例显示了如何使用 String.join 来实现这一点

> String.join "-" ["a","b","c","d","e","f"]
"a-b-c-d-e-f" : String

可以使用管道运算符 |> 执行相同的操作。管道运算符可用于链接多个函数调用。

> ["a","b","c","d","e","f"] |> String.join "-"
"a-b-c-d-e-f" : String
> ["a","b","c","d","e","f"] |> List.reverse |> String.join "-"
"f-e-d-c-b-a" : String

在第一个示例中,我们将列表链接到 join 方法。在第二种情况下,相同的列表被传递给 reverse 函数,然后传递给 join。因此,列表以反向显示并连接。

Elm - 字符串

一系列 Unicode 字符称为字符串。在 Elm 中,字符串用 "" 双引号 括起来。字符串是一块文本,如下所示。

> "TutorialsPoint"
"TutorialsPoint" : String
> location = "Hyderabad" --variable
"Hyderabad" : String
> location
"Hyderabad" : String
>

字符串函数

下面给出了一些可用于查询或操作字符串值的常用函数。使用 REPL 尝试以下示例。

序号 方法 描述
1 isEmpty : String -> Bool 检查字符串是否为空
2 reverse : String -> String 反转输入字符串
3 length : String -> Int 返回整数长度
4 append :String -> String -> String 追加两个字符串并返回一个新字符串
5 append :String -> Sconcat : List String -> String 追加字符串列表并返回一个新字符串
6 split : String -> String -> List String 使用给定的分隔符分割输入字符串,返回一个字符串列表
7 slice : Int -> Int -> String -> String 给定起始、结束索引和输入字符串,返回子字符串
8 contains : String -> String -> Bool 如果第二个字符串包含第一个字符串,则返回 true
9 toInt : String -> Result.Result String Int 将字符串解析为整数
10 toInt : String -> Result.Result String Int 将字符串解析为整数
11 toFloat : String -> Result.Result String Float 将字符串解析为浮点数
12 fromChar : Char -> String 根据给定的字符创建一个字符串。
13 toList : String -> List Char 将字符串转换为字符列表
14 fromList : List Char -> String 将字符列表转换为字符串
15 toUpper : String -> String 将输入字符串转换为大写
16 trim : String -> String 去除字符串两侧的空格。
17 filter : (Char -> Bool) -> String -> String 从输入字符串中过滤字符集
18 map : (Char -> Char) -> String -> String 转换输入字符串中的每个字符

isEmpty

此函数可用于确定字符串是否为空。如果提供的字符串为空,则此函数返回 True。

语法

String.isEmpty String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.isEmpty
<function> : String -> Bool

函数的签名显示 Bool 作为返回类型,输入类型为 String:

图示

> String.isEmpty ""
True : Bool
> String.isEmpty "Tutorialspoint"
False : Bool
> location = "Hyderabad"
"Hyderabad" : String
> String.isEmpty location
False : Bool

reverse

此函数反转字符串。

语法

String.reverse String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.reverse
<function> : String -> String

函数的签名显示 String 作为返回类型,输入类型为 String:

图示

> String.reverse "TutorialsPoint"
"tnioPslairotuT" : String

length

此函数返回字符串的长度。

语法

String.length String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.length
<function-> : String -> Int

函数的签名显示 Int 作为返回类型,输入类型为 String。

图示

> String.length "Mohtashim"
9 : Int

append

此函数通过追加两个字符串返回一个新字符串。

语法

String.append String_value1 String_value2

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.append
<function-> : String -> String -> String

签名的显示有两个 String 输入参数和一个 String 输出参数

图示

> String.append "Tutorials" "Point"
TutorialsPoint : String

concat

此函数通过将多个字符串连接成一个返回一个新字符串。

语法

String.concat [String1,String2,String3]

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.concat
<function> : List String -> String

签名的显示一个 String 列表输入参数和 String 返回类型

图示

> String.concat ["Hello","Tutorials","Point"]
HelloTutorialsPoint : String

split

此函数使用给定的分隔符拆分字符串。

语法

String.split string_seperator String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.split
<function> : String -> String -> List String

签名的显示两个输入 String 参数,输出为字符串类型列表。

图示

> String.split "," "Hello,Tutorials,Point"
["Hello","Tutorials","Point"] : List String

slice

此函数根据起始和结束索引返回子字符串。负索引从列表末尾开始获取。索引值从零开始。

语法

String.slice start_index end_index String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.slice
<function> : Int -> Int -> String -> String

签名的显示三个输入参数和一个返回类型。

图示

> String.slice 0 13 "TutorialsPoint"
"TutorialsPoin" : String

contains

如果第二个字符串包含第一个字符串,则此函数返回 True。

语法

String.contains string1 string2

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.contains
<function> : String -> String -> Bool

签名的显示 bool 返回类型和两个输入参数

图示

> String.contains "Point" "TutorialsPoint"
True : Bool

toInt

此函数将字符串转换为整数。

语法

String.toInt string_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.toInt
<function> : String -> Result.Result String Int

由于 toInt 可能返回错误,因此返回类型为 Result,即 String 或 Int。

图示

> String.toInt "20"
Ok 20 : Result.Result String Int
> String.toInt "abc"
Err "could not convert string 'abc' to an Int" : Result.Result String Int

toFloat

此函数将字符串转换为浮点数。

语法

String.toFloat string_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.toFloat
<function> : String -> Result.Result String Float

由于 toFloat 可能返回错误,因此返回类型为 Result,即 String 或 Float。

图示

> String.toFloat "20.50"
Ok 20.5 : Result.Result String Float
> String.toFloat "abc"
Err "could not convert string 'abc' to a Float" : Result.Result String Float

fromChar

此函数根据给定的字符创建一个字符串。

语法

String.fromChar character_value

要检查函数的签名,请在 elm REPL 中键入以下内容:

> String.fromChar
<function> : Char -> String

签名显示 String 作为返回类型,输入为 Char 类型

图示

> String.fromChar 'c'
"c" : String

toList

此函数将字符串转换为字符列表。

语法

String.toList string_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.toList
<function> : String -> List Char

签名显示函数返回字符列表,并以字符串作为输入。

图示

> String.toList "tutorialspoint"
['t','u','t','o','r','i','a','l','s','p','o','i','n','t'] : List Char

fromList

此函数将字符列表转换为字符串。

语法

String.fromList list_of_characters

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.fromList
<function> : List Char -> String

签名显示函数返回字符列表,并以字符串作为输入。

图示

> String.fromList ['h','e','l','l','o']
"hello" : String

toUpper

此函数将字符串转换为全部大写。

语法

String.toUpper String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.toUpper
<function> : String -> String

图示

> String.toUpper "hello"
"HELLO" : String

toLower

此函数将字符串转换为全部小写。

语法

String.toLower String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.toLower
<function> : String -> String

图示

> String.toLower "AbCd"
"abcd" : String

trim

此函数去除字符串两侧的空格。

语法

String.trim String_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.trim
<function> : String -> String

图示

> String.trim "tutorialspoint "
"tutorialspoint" : String

filter

此函数从输入字符串中过滤一组字符。仅保留通过测试的字符。

语法

String.filter test_function string_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.filter
<function> : (Char -> Bool) -> String -> String

签名显示 filter 接受两个输入参数并返回一个字符串。第一个参数是一个函数,它以 Char 作为输入并返回 Bool。

图示

在示例中,我们将 Char.isUpper 作为参数传递给 filter 方法;它返回所有大写字符,如下所示。

> import Char
> String.filter Char.isUpper "abcDEF"
"DEF" : String

map

此函数获取一个字符串并转换字符串中的每个字符。

语法

String.filter mapping_function string_value

要在 elm REPL 中检查函数的签名,请键入以下内容:

> String.map
<function> : (Char -> Char) -> String -> String

图示

以下示例将字符 o 替换为 @:

> String.map (\c -> if c == 'o' then '@' else c) "TutorialsPoint"
"Tut@rialsP@int" : String

Elm - 列表

列表、元组和记录数据结构可用于存储值的集合。

本章讨论如何在 Elm 中使用列表。

列表是同类值的集合。列表中的值必须全部为相同的数据类型。

在使用变量存储值时,请考虑以下限制:

  • 变量本质上是标量。换句话说,在声明时,一个变量只能保存一个值。这意味着要在程序中存储 n 个值,需要 n 个变量声明。因此,当需要存储大量值时,使用变量不可行。

  • 程序中的变量以随机顺序分配内存,从而难以按其声明顺序检索/读取值。

语法

List_name = [value1,value2,value3.....valuen]

图示

以下示例显示如何在 Elm 中使用列表。在 elm REPL 中尝试此示例:

> myList1 = [10,20,30]
[10,20,30] : List number
> myList2 = ["hello","world"]
["hello","world"] : List String

如果尝试将不同类型的值添加到列表中,编译器将抛出类型不匹配错误。如下所示。

> myList = [1,"hello"]
-- TYPE MISMATCH 
--------------------------------------------- 
repl-temp-000.elm

The 1st and 2nd entries in this list are different types of values.

4| [1,"hello"]
^^^^^^^
The 1st entry has this type:
   number
But the 2nd is:
   String

列表操作

下表显示了列表上的常见操作:

序号 方法 描述
1 isEmpty : List a -> Bool 检查列表是否为空
2 reverse : List a -> Bool 反转输入列表
3 length : List a -> Int 返回列表的大小
4 maximum : List comparable -> Maybe.Maybe comparable 返回最大值
5 minimum : List comparable -> Maybe.Maybe comparable 返回最小值
6 sum : List number -> number 返回列表中所有元素的总和
7 product : List number -> number 检查列表是否为空
8 sort : List comparable -> List comparable 按升序排序列表
9 concat : List (List a) -> List a 将多个列表合并成一个
10 append : List a -> List a -> List a 将两个列表合并在一起
11 range : Int -> Int -> List Int 返回从开始到结束的数字列表
12 filter : (a -> Bool) -> List a -> List a 从输入列表中过滤值列表
13 head : List a -> Maybe.Maybe a 返回列表中的第一个元素
14 tail : : List a -> Maybe.Maybe (List a) 返回除头部之外的所有元素

isEmpty

如果列表为空,则此函数返回 true。

语法

List.isEmpty list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.isEmpty
<function> : List a -> Bool

图示

> List.isEmpty
<function> : List a -> Bool

> List.isEmpty [10,20,30]
False : Bool

reverse

此函数反转列表。

语法

List.reverse list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.reverse
<function> : List a -> List a

图示

> List.reverse [10,20,30]
[30,20,10] : List number

length

此函数返回列表的长度。

语法

List.length list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.length
<function> : List a -> Int

图示

> List.length [10,20,30]
3 : Int

maximum

此函数返回非空列表中的最大元素。

语法

List.maximum list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.maximum
<function> : List comparable -> Maybe.Maybe comparable

图示

> List.maximum [10,20,30]
Just 30 : Maybe.Maybe number
> List.maximum []
Nothing : Maybe.Maybe comparable

minimum

此函数返回非空列表中的最小元素。

语法

List.minimum list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.minimum
<function> : List comparable -> Maybe.Maybe comparable

图示

> List.minimum [10,20,30]
Just 10 : Maybe.Maybe number

sum

此函数返回列表中所有元素的总和。

语法

List.sum list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.sum
<function> : List number -> number

图示

> List.sum [10,20,30]
60 : number

product

此函数返回列表中所有元素的乘积。

语法

List.product list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

<function>  : List number ->  number

图示

List.product [10,20,30]
6000 : number

sort

此函数将列表中的值从低到高排序。

语法

List.sort list_name

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.sort
<function> : List comparable -> List comparable

图示

> List.sort [10,20,30]
[10,20,30] : List number

concat

此函数将多个列表连接成一个列表。

语法

List.concat [ [list_name1],[list_name2],[list_name3],.....[list_nameN] ]

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.concat
<function> : List (List a) -> List a

图示

> List.concat [[10,20], [30,40],[50,60]]
[10,20,30,40,50,60] : List number

append

此函数将两个列表放在一起。

语法

List.append [list_name1] [list_name2]

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.append
<function> : List a -> List a -> List a

图示

> List.append [10,20] [30,40]
[10,20,30,40] : List number

++ 运算符也可用于将一个列表追加到另一个列表。如下面的示例所示:

> [10.1,20.2] ++ [30.3,40.4]
[10.1,20.2,30.3,40.4] : List Float

range

此函数创建一个数字列表,每个元素增加 1。应在列表中包含的最小和最大数字传递给函数。

语法

List.range start_range end_range

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.range
<function> : Int -> Int -> List Int

图示

> List.range 1 10
[1,2,3,4,5,6,7,8,9,10] : List Int

filter

此函数从输入列表中过滤一组值。仅保留通过测试的值。

语法

List.filter test_function input_list

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.filter
<function> : (a -> Bool) -> List a -> List a

图示

以下示例从输入列表中过滤所有偶数

> List.filter (\n -> n%2==0) [10,20,30,55]
[10,20,30] : List Int

head

此函数返回输入列表中的第一个元素。

语法

List.head input_list

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.head
<function> : List a -> Maybe.Maybe a

图示

> List.head [10,20,30,40]
Just 10 : Maybe.Maybe number
> List.head []
Nothing : Maybe.Maybe a

tail

此函数返回列表中第一个元素之后的所有元素。

语法

List.tail input_list

要在 elm REPL 中检查函数的签名,请键入以下内容:

> List.tail
<function> : List a -> Maybe.Maybe (List a)

图示

> List.tail [10,20,30,40,50]
Just [20,30,40,50] : Maybe.Maybe (List number)
> List.tail [10]
Just [] : Maybe.Maybe (List number)
> List.tail []
Nothing : Maybe.Maybe (List a)

使用 Cons 运算符

cons 运算符 ( :: ) 将元素添加到列表的前面。

图示

> 10::[20,30,40,50]
[10,20,30,40,50] : List number

要添加的新元素和列表中值的​​数据类型必须匹配。如果数据类型不匹配,编译器将抛出错误。

> [1,2,3,4]::[5,6,7,8]
-- TYPE MISMATCH ---------------------------------
------------ repl-temp-000.elm

The right side of (::) is causing a type mismatch.

3| [1,2,3,4]::[5,6,7,8]
			  ^^^^^^^^^
(::) is expecting the right side to be a:

   List (List number)

But the right side is:

   List number
Hint: With operators like (::) I always check the left side first. If it seems fine, 
I assume it is correct and check the right side. So the 
problem may be in how the left and right arguments interact.

列表是不可变的

让我们检查 Elm 中的列表是否不可变。当第一个列表 myList 与值 1 连接时,会创建一个新列表并将其返回到 myListCopy。因此,如果我们显示初始列表,则其值将不会更改。

> myList = [10,20,30]
[10,20,30] : List number
> myListCopy = 1::myList
[1,10,20,30] : List number
> myList
[10,20,30] : List number
>myList == myListCopy
False : Bool

Elm - 元组

有时,可能需要存储各种类型的值的集合。Elm 为我们提供了一种名为元组的数据结构来实现此目的。

元组表示异类值的集合。换句话说,元组能够存储不同类型的多个字段。元组存储固定数量的值。当您希望从函数返回不同类型的多个值时,元组很有用。这些数据结构与 elm 中的其他类型一样是不可变的。

语法

(data1,data2)

一个简单的示例如下所示:

> ("TuotrialsPoint",5,True,"Hyderabad")
("TuotrialsPoint",5,True,"Hyderabad") : ( String, number, Bool, String )

在我们接下来的章节中,我们将学习不同的元组操作。

first

此操作从元组中提取第一个值。

语法

Tuple.first tuple_name
> Tuple.first
<function> : ( a1, a2 ) -> a1

图示

> Tuple.first (10,"hello")
10 : number

second

second 元组操作从元组中提取第二个值。

语法

Tuple.second tuple_name
> Tuple.second
<function> : ( a1, a2 ) -> a2

图示

> Tuple.second (10,"hello")
"hello" : String

元组列表

列表可以存储元组。如果在列表中使用元组,请确保它们都具有相同的数据类型并具有相同数量的参数。

图示

> [("hello",20),("world",30)]
[("hello",20),("world",30)] : List ( String, number )

带有函数的元组

函数可以返回元组。此外,元组可以作为参数传递给函数。

插图 1

以下示例定义了一个函数 fn_checkEven。此函数接受一个整数作为参数并返回一个元组。

> fn_checkEven no = \
   if no%2 == 0 then \
      (True,"The no is Even")\
   else \
      (False,"No is not even")
<function> : Int -> ( Bool, String )
> fn_checkEven 10
(True,"The no is Even") : ( Bool, String )
> fn_checkEven 11
(False,"No is not even") : ( Bool, String )
>

插图 2

以下将元组作为参数传递给函数。

> fn_add (a,b) = \
| a+b
<function> : ( number, number ) -> number
> fn_add (10,20)
30 : number

函数 fn_add 获取一个包含 2 个数值的元组并返回它们的总和。

解构

解构涉及将元组分解成单个值。要访问具有三个或更多元素的元组中的单个值,我们使用解构。在这里,我们将元组中的每个值分配给不同的变量。使用 _ 可以为将被忽略或跳过的值定义占位符。

图示

> (first,_,_) = (10,20,30)
10 : number
> first
10 : number

图示

在此示例中,我们将使用 let..in 块语法进行解构。let 块包含变量,in 块包含应计算的表达式和应返回的值。

> t1 = (10,20,30)
(10,20,30) : ( number, number1, number2 )
> let \
(a,b,c) = t1 \
in\
a + b +c
60 : number

我们在 let 子句中声明变量 a b c,并在 in 子句中使用它们。

Elm - 记录

Elm 中的记录数据结构可用于表示数据作为键值对。记录可用于组织相关数据,以方便访问和更新数据。Elm 记录类似于 JavaScript 中的对象。记录中的数据元素称为字段。

定义记录

使用以下语法定义记录:

语法

record_name = {fieldname1 = value1, fieldname2 = value2....fieldnameN = valueN}

记录可以存储多种类型的数据。记录中的字段名称必须符合 Elm 标识符命名的一般规则。

访问记录值

使用以下语法访问记录中的各个字段。

语法

record_name.fieldname

.fieldname record_name

图示

在 Elm REPL 中尝试以下操作:

> company = {name="TutorialsPoint",rating=4.5}
{ name = "TutorialsPoint", rating = 4.5 } : { name : String, rating : Float }
> company.name
"TutorialsPoint" : String
> .rating company
4.5 : Float

将记录与列表一起使用

记录可以存储在列表中。记录的所有字段值都应为相同类型。

语法

list_name = [ {field_name1 = value1},{field_name1 = value2}]

list_name = [record_name1, record_name2, record_name3....record_nameN]

图示

在 Elm REPL 中尝试以下操作:

> [{name = "Mohtashim"},{name = "kannan"}]
[{ name = "Mohtashim" },{ name = "kannan" }] : List { name : String }
> record1 = {name = "FirstRecord"}
{ name = "FirstRecord" } : { name : String }
> record2 = {name = "SecondRecord"}
{ name = "SecondRecord" } : { name : String }
> recordList = [record1,record2]
[{ name = "FirstRecord" },{ name = "SecondRecord" }] : List { name : String }

更新记录

记录在 Elm 中是不可变的。当记录被更新时,会返回一个具有更新值的新记录。在更新记录时,字段可以保存不同类型的值。

语法

{record_name | field_name1 = new_value1, field_name2 = new_value2,field_name3 = new_value3....field_nameN = new_valueN}

图示

在 Elm REPL 中尝试以下操作:

> record1 = {name="FirstRecord"}
{ name = "FirstRecord" } : { name : String }
> record1_updated = {record1 | name = "FirstRecordUpdate"}
{ name = "FirstRecordUpdate" } : { name : String }
> record1
{ name = "FirstRecord" } : { name : String }
> record1 == record1_updated
False : Bool

图示

以下示例更新记录的多个字段。在 Elm REPL 中尝试以下操作:

> record3 = {a = 1,b = 2,c = 3,d = 4,e = 5}
{ a = 1, b = 2, c = 3, d = 4, e = 5 }
: { a : number, b : number1, c : number2, d : number3, e : number4 }
> record4 = {record3 | d=400 ,e=500}
{ a = 1, b = 2, c = 3, d = 400, e = 500 }
: { a : number2, b : number3, c : number4, d : number, e : number1 }
>

类型别名

类型别名定义记录的模式。换句话说,类型别名定义了记录可以存储哪些字段以及这些字段可以存储哪种类型的值。因此,程序员在赋值时不会犯遗漏任何特定属性的错误。

语法

type alias alias_name = {field_name1:data_type,field_name2:data_type,....field_nameN:data_type}

图示

在 Elm REPL 中执行以下操作:

> type alias Developer = { name:String,location:String,age:Int}
> dev1 = Developer "kannan" "Mumbai" 20
{ name = "kannan", location = "Mumbai", age = 20 } : Repl.Developer
> dev2 = Developer "mohtashim" "hyderabad" 20
{ name = "mohtashim", location = "hyderabad", age = 20 } : Repl.Developer
>

现在,如果您忘记键入 location 和 age,该语句将返回一个函数,该函数具有 location 和 age 字段的输入参数。

> dev3 = Developer "Bhagavati"
<function> : String -> Int -> Repl.Developer
We can invoke the function as shown below and pass to it the values for location and age fields.
> dev3 "Pune" 25
{ name = "Bhagavati", location = "Pune", age = 25 } : Repl.Developer

Elm - 错误处理

错误是在程序中出现的任何意外情况。错误可能发生在编译时或运行时。编译时错误发生在程序编译期间(例如,程序语法错误),而运行时错误发生在程序执行期间。与其他编程语言不同,Elm 不会抛出运行时错误。

考虑一个接受用户年龄的应用程序。如果年龄为零或负数,则应用程序应抛出错误。在这种情况下,Elm 应用程序可以使用错误处理的概念在运行时显式地引发错误,如果用户输入零或负值作为年龄。错误处理指定了如果在程序执行期间发生任何意外情况时要采取的行动。

Elm 编程语言以以下方式处理错误:

  • MayBe
  • Result

MayBe

考虑应用程序中的搜索功能。如果找到搜索关键字,则搜索函数返回相关数据,否则不返回任何内容。此用例可以使用 MayBe 类型在 Elm 中实现。

语法

variable_name:MayBe data_type

MayBe 类型的变量可以包含以下值之一:

  • Just some_Value - 如果有有效数据,则使用此值。

  • Nothing - 如果值不存在或未知,则使用此值。Nothing 等价于其他编程语言中的 null。

图示

以下示例显示了如何在变量和函数中使用 MayBe 类型。

步骤 1 - 创建一个 MayBeDemo.elm 文件并向其中添加以下代码

-- MayBeDemo.elm
module MayBeDemo exposing(..)
import Maybe

--declaring a MayBe variable and assigning value to it
userName : Maybe String
userName = Just "Mohtashim"

--declaring a MayBe variable and assigning value to it
userAge :Maybe Int
userAge = Just 20

--declaring a MayBe variable and assigning value to it
userSalary:Maybe Float
userSalary = Nothing

--declaring a custom type
type Country = India | China | SriLanka

--defining a function that takes a String parameter as input and returns a value of type MayBe

getCountryFromString : String -> Maybe Country
getCountryFromString p =
case p of
   "India"
      -> Just India
   "China"
      -> Just China
   "SriLanka"
      -> Just SriLanka
   _
      -> Nothing

步骤 2 - 在 elm repl 中导入模块并按如下所示执行

E:\ElmWorks\ErroApp> elm repl
---- elm-repl 0.18.0 -----------------------------------------------------------
:help for help, :exit to exit, more at 
--------------------------------------------------------------------------------
> import MayBeDemo exposing(..)
> userName
Just "Mohtashim" : Maybe.Maybe String
> userAge
Just 20 : Maybe.Maybe Int
> userSalary
Nothing : Maybe.Maybe Float
> getCountryFromString "India"
Just India : Maybe.Maybe MayBeDemo.Country
> getCountryFromString "india"
Nothing : Maybe.Maybe MayBeDemo.Country

该函数检查传递给函数的值是否为印度、中国或斯里兰卡。如果参数的值与这些值都不匹配,则返回 nothing。

Result

考虑一个示例,其中应用程序需要验证某些条件,如果不满足条件则引发错误。可以使用 Result 类型来实现此目的。如果应用程序想要显式地引发错误并返回有关错误原因的详细信息,则应使用 Result 类型。

语法

Result 类型声明接受两个参数 - 错误的数据类型(通常为 String)以及如果一切顺利则要返回的结果的数据类型。

type Result error_type data_value_type
= Ok data_value
| Err error_message

Result 类型返回以下值之一:

  • Ok some_value - 表示要返回的结果

  • Err - 表示如果未满足预期条件则要返回的错误消息。

插图 1

在 Elm REPL 中尝试以下示例 -

> String.toInt
<function> : String -> Result.Result String Int
-- successful result
> String.toInt "10"
Ok 10 : Result.Result String Int
-- unsuccessful result , Error
> String.toInt "a"
Err "could not convert string 'a' to an Int" : Result.Result String Int

String.toInt 函数如果传递的参数有效则返回 Integer 值。如果参数不是数字,则函数返回错误。

插图 2

以下示例接受年龄作为参数。如果年龄在 0 到 135 之间,则该函数返回年龄,否则返回相应的错误消息。

步骤 1 - 创建一个 ResultDemo.elm 文件并向其中添加以下代码。

--ResultDemo.elm
module ResultDemo exposing(..)

userId : Result String Int
userId = Ok 10

emailId : Result String Int
emailId = Err "Not valid emailId"

isReasonableAge : String -> Result String Int
isReasonableAge input =
   case String.toInt input of
      Err r ->
         Err "That is not a age!"

   Ok age ->
      if age < 0 then
         Err "Please try again ,age can't be negative"
      else if age > 135 then
         Err "Please try agian,age can't be this big.."

   else
      Ok age

步骤 2 - 在 elm 包中导入模块并按如下所示执行

E:\ElmWorks\ElmRepo\15_ErrorHandling\15_Code> elm repl
---- elm-repl 0.18.0 -----------------------------------------------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
> import ResultDemo exposing (..)
> userId
Ok 10 : Result.Result String Int
> emailId
Err "Not valid emailId" : Result.Result String Int
> isReasonableAge "10"
Ok 10 : Result.Result String Int
> isReasonableAge "abc"
Err "That is not a age!" : Result.Result String Int

Elm - 架构

在本章中,我们将讨论在 Elm 平台上创建应用程序的标准方法。Elm 使用类似于 Model-View-Controller 模式的架构模式。

以下是 Elm 架构的四个主要部分。

  • 模型 (Model)
  • 视图 (View)
  • 消息 (Message)
  • 更新 (Update)
Architecture

Elm 架构如何工作

模型包含应用程序状态。例如,如果应用程序显示客户列表,则状态将包含每个客户数据。为了以可呈现的方式显示状态,必须生成一个视图/html。一旦用户通过按下按钮或在表单中键入数据与视图交互,视图就会生成称为消息的信号。消息传递给更新方法,该方法评估消息并采取适当的措施。因此,更新方法将生成一个新的模型。

新模型生成一个新的视图。视图将导致用户发出新的交互以发出消息,这些消息将传递到更新函数。此外,该函数会创建一个新模型。因此,循环重复,如上图所示。

模型 (Model)

模型处理应用程序的状态。定义模型的语法如下所示:

-- Model syntax

type alias Model = {
   property1:datatype,
   proptery2:datatype
...
}

要创建模型,我们需要首先创建一个包含所有所需属性的模板。每个属性都指定应用程序的状态。

视图 (View)

视图是应用程序状态的视觉表示。视图知道如何获取数据并从中生成网页。当用户与视图交互时,用户可以通过生成消息来操作状态。定义视图的语法如下所示:

--View Syntax
view model =some_implementation

消息 (Message)

消息是用户更改应用程序状态的请求。消息作为参数传递给更新函数。

--Message Syntax
type Message = Message1 |Message2 ...

语法显示了一个 Message 类型。elm 应用程序将根据传递给它的消息来编辑状态。这些决策是在更新方法中做出的。

更新 (Update)

更新函数解释作为参数传递给它的消息,并更新模型。

--Update Syntax
update Message_type model =
   some_implementation

更新函数将消息和模型作为参数。

Elm - 包管理器

包管理器是一个命令行工具,它自动化了在应用程序中安装、升级、配置和删除包的过程。

就像 JavaScript 有一个名为 npm 的包管理器一样,elm 有一个名为elm-package的包管理器。

包管理器执行以下三个任务:

  • 安装 elm 应用程序所需的所有依赖项
  • 发布自定义包
  • 在您准备好发布和更新时确定包的版本。

Elm 包管理器命令

下表列出了各种 Elm 包管理器命令:

序号 命令 语法 描述
1 安装 (install) elm-package install 安装要本地使用的包
2 发布 (publish) elm-package publish 将您的包发布到中央目录
3 更新版本 (bump) elm-package bump 根据 API 更改更新版本号
4 差异 (diff) elm-package diff 获取两个 API 之间的差异

为了发布您的包,您需要在 GitHub 上托管源代码,并使用 git 标签正确标记版本。下图显示了如何使用 elm-package 管理器拉取外部依赖项。

图示 - 安装 svg 包

在此示例中,我们将了解如何将可缩放矢量图形 (SVG) 集成到 elm 应用程序中。

步骤 1 - 创建一个文件夹 elmSvgApp

步骤 2 - 使用以下命令安装 svg 包:

elm-package install elm-lang/svg

步骤 3 - 安装 创建一个 SvgDemo.elm 文件并键入以下内容。我们导入 Svg 模块以绘制一个100x100尺寸的矩形并填充红色。

import Svg exposing (..)
import Svg.Attributes exposing (..)

main =
   svg
   [ width "120"
   , height "120"
   , viewBox "0 0 120 120"
   ]
   [ rect
      [ x "10"
      , y "10"
      , width "100"
      , height "100"
      , rx "15"
      , ry "15"
      ,fill "red"
      ]
      []
   ]

步骤 4 - 现在使用 elm make .\SvgDemo.elm 构建项目。这将生成一个 index.html,如下所示:

build project

Elm - 消息

消息是 Elm 架构中的一个组件。这些组件由视图响应用户与应用程序界面的交互而生成。消息表示用户更改应用程序状态的请求。

语法

--Message Syntax
type Message = some_message1 |some_message2 ...|some_messageN

图示

以下示例是一个简单的计数器应用程序。当用户分别点击“添加”和“减去”按钮时,该应用程序会将变量的值增加或减少 1。

该应用程序将有 4 个组件。组件如下所述:

消息 (Message)

此示例的消息将为:

type Message = Add | Subtract

模型 (Model)

模型表示应用程序的状态。在计数器应用程序中,模型定义如下;计数器的初始状态将为零。

model = 0

视图 (View)

视图表示应用程序的视觉元素。视图包含两个按钮(+)和(-)。当用户分别点击 + 和 - 按钮时,视图会生成 Add 和 Subtract 消息。然后,视图显示模型的修改值。

view model =
-- invoke text function
h1[]
[   div[] [text "CounterApp from TutorialsPoint" ]
   ,button[onClick Subtract] [text "-"]
   ,div[][text (toString model)]
   ,button[onClick Add] [text "+"]
]

更新 (Update)

此组件包含应为视图生成的每个消息执行的代码。这在下面的示例中显示:

update msg model =
case msg of
Add -> model+1
Subtract -> model-1

将所有内容放在一起

步骤 1 - 创建一个文件夹MessagesApp 和文件 MessagesDemo.elm

步骤 2 - 在 elm 文件中添加以下代码:

import Html exposing (..)
import Html.Events exposing(onClick)

model = 0 -- Defining the Model

--Defining the View

view model =
   h1[]
   [  div[] [text "CounterApp from TutorialsPoint" ]
      ,button[onClick Subtract] [text "-"]
      ,div[][text (toString model)]
      ,button[onClick Add] [text "+"]
   ]

--Defining the Messages

type Message = Add | Subtract

--Defining Update

update msg model =
case msg of
   Add -> model+1
   Subtract -> model-1

-- Define the main method
main =
   beginnerProgram
   {
      model=model
      ,view=view
      ,update=update
   }

步骤 3 - 在终端中执行elm make 命令elm make 命令编译代码并从上面创建的 .elm 文件生成 HTML 文件。

C:\Users\dell\elm\MessagesApp> elm make .\MessageDemo.elm
Some new packages are needed. Here is the upgrade plan.

   Install:
      elm-lang/core 5.1.1
      elm-lang/html 2.0.0
      elm-lang/virtual-dom 2.0.4

Do you approve of this plan? [Y/n] y
Starting downloads...

   ΓùÅ elm-lang/html 2.0.0
   ΓùÅ elm-lang/virtual-dom 2.0.4

ΓùÅ elm-lang/core 5.1.1
Packages configured successfully!
Success! Compiled 38 modules.
Successfully generated index.html

步骤 4 - 打开index.html并验证工作情况,如下所示:

elm make command

Elm - 命令

在前面的章节中,我们讨论了 Elm 架构的各个组件及其功能。用户和应用程序使用消息相互通信。

考虑一个示例,其中应用程序需要与其他组件(如外部服务器、API、微服务等)通信以服务用户请求。这可以通过在 Elm 中使用命令来实现。消息和命令不是同义词。消息表示最终用户与应用程序之间的通信,而命令表示 Elm 应用程序如何与其他实体通信。命令是响应消息触发的。

下图显示了复杂 Elm 应用程序的工作流程:

Workflow

用户与视图交互。视图根据用户的操作生成相应的消息。更新组件接收此消息并触发命令。

语法

定义命令的语法如下所示:

type Cmd msg

视图生成的消息传递给命令。

图示

以下示例向 API 发出请求并显示 API 返回的结果。

该应用程序从用户处接受一个数字,将其传递给 Numbers API。此 API 返回与数字相关的 facts。

应用程序的各个组件如下:

Http 模块

Elm 的 Http 模块用于创建和发送 HTTP 请求。此模块不是核心模块的一部分。我们将使用 elm 包管理器来安装此包。

API

在此示例中,应用程序将与 Numbers API 通信 - "http://numbersapi.com/#42"。

视图 (View)

应用程序的视图包含一个文本框和一个按钮。

view : Model -> Html Msg
view model =
   div []
      [ h2 [] [text model.heading]
      ,input [onInput Input, value model.input] []
      , button [ onClick ShowFacts ] [ text "show facts" ]
      , br [] []
      , h3 [][text model.factText]
      ]

模型 (Model)

模型表示用户输入的值以及 API 将返回的结果。

type alias Model =
   { heading : String
   , factText : String
   , input :String
   }

消息 (Message)

该应用程序有以下三个消息:

  • ShowFacts
  • Input
  • NewFactArrived

单击Show Facts按钮时,ShowFacts消息将传递到更新方法。当用户在文本框中键入某些值时,Input消息将传递到更新方法。最后,当收到 Http 服务器响应时,NewFactArrived消息将传递到更新。

type Msg
   = ShowFacts
   |Input String
   | NewFactArrived (Result Http.Error String)

更新 (Update)

更新方法返回一个元组,其中包含模型和命令对象。当用户单击 Show Facts 按钮时,消息将传递到更新,然后更新调用 NumbersAPI。

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
   case msg of
      Input newInput ->
      (Model "NumbersApi typing.." "" newInput ,Cmd.none)
      ShowFacts ->
         (model, getRadmonNumberFromAPI model.input)

      NewFactArrived (Ok newFact) ->
         (Model "DataArrived" newFact "", Cmd.none)

      NewFactArrived (Err _) ->
      (model, Cmd.none)

辅助函数

辅助函数getRandomNumberFromAPI调用 NumbersAPI 并将用户输入的数字传递给它。API 返回的结果用于更新模型。

getRadmonNumberFromAPI : String->Cmd Msg
getRadmonNumberFromAPI newNo =
   let
      url =
         "http://numbersapi.com/"++newNo
   in
      Http.send NewFactArrived (Http.getString url)
序号 方法 签名 描述
1 Http.getString getString : String -> Request String 创建一个 GET 请求并将响应正文解释为字符串。
2 Http.send

发送:(Result Error a -> msg) -> Request a -> Cmd msg 发送一个 HTTP 请求。

主函数

这是 Elm 项目的入口点。

main =
   Html.program
      { init = init
      , view = view
      , update = update
      , subscriptions = subscriptions
      }

将所有内容放在一起

步骤 1 − 创建文件夹 CommandApp 和文件 CommandDemo.elm。

步骤 2 − 使用命令 elm package install elm-lang/http 安装 http 模块。

步骤 2 − 输入 CommandDemo.elm 的内容,如下所示:

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http

main =
   Html.program
      { init = init
      , view = view
      , update = update
      , subscriptions = subscriptions
      }

-- MODEL
type alias Model =
   { heading : String
   , factText : String
   , input :String
   }

init : (Model, Cmd Msg)
init =
   ( Model "NumbersAPI" "NoFacts" "42"-- set model two fields
   , Cmd.none -- not to invoke api initially
   )

-- UPDATE

type Msg
   = ShowFacts
   |Input String
   | NewFactArrived (Result Http.Error String)

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
   case msg of
      Input newInput ->
      (Model "NumbersApi typing.." "" newInput ,Cmd.none)
      ShowFacts ->
         (model, getRadmonNumberFromAPI model.input)

      NewFactArrived (Ok newFact) ->
         (Model "DataArrived" newFact "", Cmd.none)

      NewFactArrived (Err _) ->
         (model, Cmd.none)

- VIEW

view : Model -> Html Msg
view model =
   div []
      [ h2 [] [text model.heading]
      ,input [onInput Input, value model.input] []
      , button [ onClick ShowFacts ] [ text "show facts" ]
      , br [] []
      , h3 [][text model.factText]
      ]

-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions model =
   Sub.none

-- HTTP

getRadmonNumberFromAPI : String->Cmd Msg
getRadmonNumberFromAPI newNo =
   let
      url =
      "http://numbersapi.com/"++newNo
   in
      Http.send NewFactArrived (Http.getString url)

步骤 4 − 执行命令。

C:\Users\dell\elm\CommandApp> elm make .\CommandDemo.elm

这将生成如下所示的 html 文件。

Generate html

Elm - 订阅

在上一章中,我们讨论了 View 如何使用命令与其他组件交互。类似地,组件(例如 WebSocket)可以通过订阅与 View 交互。订阅是 Elm 应用程序接收外部输入(如键盘事件、计时器事件和 WebSocket 事件)的一种方式。

下图说明了订阅在 Elm 应用程序中的作用。用户通过消息与 Elm 应用程序交互。给定的应用程序使用 WebSocket,并且有两种操作模式:

  • 通过命令将客户端数据发送到套接字服务器
  • 通过订阅随时接收来自套接字服务器的数据
socket server

语法

定义订阅的语法如下所示:

type Sub msg

图示

让我们通过一个简单的例子来了解订阅。

在下面的示例中,应用程序向服务器发送消息。服务器是一个回显服务器,它使用相同的消息回复客户端。所有传入的消息随后都会显示在一个列表中。我们将使用 WebSocket(wss 协议)来持续监听来自服务器的消息。WebSocket 将使用命令将用户输入发送到服务器,同时将使用订阅接收来自服务器的消息。

应用程序的各个组件如下所示:

回显服务器

可以使用 wss 协议访问回显服务器。回显服务器将用户输入发送回应用程序。定义回显服务器的代码如下所示:

echoServer : String
echoServer =
"wss://echo.websocket.org"

模型 (Model)

模型表示用户输入和来自套接字服务器的传入消息列表。定义模型的代码如下所示:

type alias Model =
   { input : String
   , messages : List String
   }

消息

消息类型将包含 Input 用于获取用户的文本输入。当用户点击按钮将消息发送到 WebSocket 服务器时,将生成 Send 消息。当从回显服务器收到消息时,将使用 NewMessage。

type Msg
   = Input String
   | Send
   | NewMessage String

视图 (View)

应用程序的视图包含一个文本框和一个提交按钮,用于将用户输入发送到服务器。服务器的响应使用 div 标签在视图中显示。

view : Model -> Html Msg
view model =
   div []
      [ input [onInput Input, value model.input] []
      , button [onClick Send] [text "Send"]
      , div [] (List.map viewMessage (List.reverse model.messages))
      ]

viewMessage : String -> Html msg
viewMessage msg =
   div [] [ text msg ]

更新 (Update)

更新函数接收消息和模型组件。它根据消息类型更新模型。

update : Msg -> Model -> (Model, Cmd Msg)
update msg {input, messages} =
   case msg of
      Input newInput ->
         (Model newInput messages, Cmd.none)

   Send ->
      (Model "" messages, WebSocket.send echoServer input)

   NewMessage str ->
      (Model input (str :: messages), Cmd.none)
序号 方法 签名 描述
1 WebSocket.listen listen : String -> (String -> msg) -> Sub msg 订阅 WebSocket 上的任何传入消息。
2 WebSocket.send send : String -> String -> Cmd msg 向服务器地址发送 wss 请求。重要的是,您也已使用 listen 订阅了此地址。如果没有,将创建 WebSocket 以发送一条消息然后关闭。

订阅

订阅函数接收模型对象。为了接收来自 WebSocket 服务器的消息,我们调用 WebSocket.listen 并将消息作为 NewMessage 传递。当从服务器收到新消息时,将调用更新方法。

subscriptions : Model -> Sub Msg
subscriptions model =
WebSocket.listen echoServer NewMessage

主函数

主函数是 Elm 应用程序的入口点,如下所示。

main =
   Html.program
      { init = init
      , view = view
      , update = update
      , subscriptions = subscriptions
      }

将所有内容放在一起

步骤 1 − 创建一个目录 SubscriptionApp 并向其中添加一个文件 SubscriptionDemo.elm。

步骤 2 − 将以下内容添加到 SubscriptionDemo.elm 文件中:

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import WebSocket

main =
   Html.program
      { init = init
      , view = view
      , update = update
      , subscriptions = subscriptions
      }

echoServer : String
echoServer =
   "wss://echo.websocket.org"

-- MODEL

type alias Model =
   { input : String
   , messages : List String
   }

init : (Model, Cmd Msg)
init =
   (Model "" [], Cmd.none)

-- UPDATE
type Msg
   = Input String
   | Send
   | NewMessage String

update : Msg -> Model -> (Model, Cmd Msg)
update msg {input, messages} =
   case msg of
      Input newInput ->
      (Model newInput messages, Cmd.none)

   Send ->
      (Model "" messages, WebSocket.send echoServer input)

   NewMessage str ->
      (Model input (str :: messages), Cmd.none)

-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
   WebSocket.listen echoServer NewMessage

-- VIEW
view : Model -> Html Msg
view model =
   div []
      [ input [onInput Input, value model.input] []
      , button [onClick Send] [text "Send"]
      , div [] (List.map viewMessage (List.reverse model.messages))
      ]

viewMessage : String -> Html msg
viewMessage msg =
div [] [ text msg ]

步骤 3 − 使用 elm 包管理器安装 websockets 包。

C:\Users\dell\elm\SubscriptionApp> elm-package install elm-lang/websocket

步骤 4 − 构建并生成 index.html 文件,如下所示。

C:\Users\dell\elm\SubscriptionApp> elm make .\SubscriptionDemo.elm

步骤 5 − 执行后,将生成以下输出:

SubscriptionApp
广告