非法状态,选项类型,函数,元组,列表,数组和相等性
这是一个分为3部分的系列文章的 第2 部分 ,我将帮助您理解ReasonML及其所有语法和语义。 我将介绍从基本数据类型到在ReasonML中声明函数的所有内容。
如果您还没有阅读第1部分 ,我建议您首先阅读,因为本系列中的所有文章都相互链接。
用变种消除非法国家
表达多个作为数据结构专有的选项是ReasonML的强大概念之一。 但是要明智地使用它,我们需要重新考虑如何对数据建模。
首先,我将创建一个简单的应用程序来获取和呈现一些数据。 对于此应用程序,我首先需要定义UI状态。 我希望应用程序在等待数据时打印加载文本,并在应用程序获取数据成功或失败时打印一些文本。
我们首先声明您的记录type request
。 我们添加了类型为bool
的字段loading
,以指示请求是否正在加载。 对error
字段执行相同的操作。 我还需要添加string
类型的name
字段。 name
是我希望我的应用程序提取的数据。
逻辑很简单。 如果请求当前正在加载,我们将返回loading
。 万一失败,我们打印Something went wrong
。 请求成功后,我们将打印name
。
# type request = {
loading: bool,
error: bool,
name: string,
};
# let myRequest = {
loading: false,
error: false,
name: "Rajat"
};
- : string = "Rajat"
但是仍然有很多地方可能出问题。 例如,如果有人将loading
和error
都设置为true
怎么办?
if/else
表达式呈现loading
,但是根据定义,它永远不会发生。 由于数据的结构方式,这提出了一个问题,在这种情况下,我们的UI代码应该做什么? 也许显示error
而不是loading
?
我们可以使用变体来解决这个问题。 我将使用变体重新创建类型请求。 每个标记代表状态之一,即loading
, error
, success
。 成功是相同的附加条件。
# type request = Loading | Error | Success(string);
设置状态并为要渲染的文本编写一个switch表达式:
# let state = Loading;
# let ui =
| Loading => "Loading..."
| Error => "Something went wrong"
| Success => "Your name is " ++ name
};
我们可以更改状态并重新运行switch表达式。 所有案例均按预期工作。 很棒的事情是,如果我们添加一个request
变量,我们将永远无法同时具有loading
, error
和success
。
将switch
表达式与variant一起使用将防止我们忘记状态。 如果您错过状态,理性会抛出一个错误。
这将解决我们的大多数问题。 但是还有另外一种情况我们需要照顾。 如果您的后端没有存储数据,该怎么办。 然后,该应用将返回一个空字符串,输出将类似于Your name is
。
为了解决这个损坏的UI,我们将匹配一个空字符串并返回适当的大小写。
# let ui =
switch(state) {
| Loading => "Loading..."
| Error => "Something went wrong"
| Success("") => "Your name is missing"
| Success => "Your name is " ++ name
};
在实际情况下, Success
构造函数将包含另一个数据结构,例如Record
或List
类的另一个变体。
# type userResponse = {
id: int,
name: string,
age: int,
};
# type request = Loading | Error | Success(userResponse);
# let state = Success({id: 1, name: "Rajat", age: 22});
注意:类型系统不能消除错误。 它只能指出未处理的条件,并要求您覆盖它们。
期权类型
理性不为null
。 如果您尝试将null
与类型为int
age
绑定,则将无法使用。
# let age: int = null;
Error Unbound value null
有人会说这很棒。 null
是导致我们的应用程序出现许多错误的原因。 但是null
确实可以达到目的。 例如,您想引用应用程序中的某些内容,但您尚不知道相应的值是否可用。
这就是为什么Reason为我们提供称为option
的变体的原因。 此变体具有两个标签:
- 无 -这表示没有值可用。
- 一些 -这表示有一个值。
# None;
- : option('a) = None
# Some(42);
- : option(int) = Some(42)
我们可以将它们与这样的name
一起使用:
# let name = None;
let name: option('a) = None;
# let name = Some("Rajat");
let name: option(string) = Some("Rajat")
像任何其他变体一样,我们也可以将其与switch
表达式一起使用。
let message =
switch (name) {
| None => Sadly I don't know"
| Some(value) => "The meaning of life is: " ++ value
};
使用option时,我们可以使用一个工具,该工具可以模拟新值,同时仍然保持类型安全,这意味着纯Reason程序不会有空错误。
功能
在Reason中声明和使用函数非常简单。 匿名函数如下所示:
# (x) => x + 1;
- : int => int = <fun>
原因中的功能由括号内的一个或多个参数定义。 如果只有一个参数,则可以省略括号。
# let plusOne = x => x + 1;
# plusOne(4);
- : int = 5
要编写一个将整数添加到浮点数的函数,我们可以执行以下操作:
# let add = (x, y) => {
let z = float_of_int(x);
y + z;
};
该语言的另一个特点是它对参数的部分应用。 我们可以通过使用两个参数调用函数来将两个整数加在一起。
# let add = (x, y) => x + y;
# add(3, 2);
- : int = 5
提供一个论点也是可能的。 它不会返回错误,而是会作为一个函数返回,该函数仅需要提供一个参数(在我们的情况下为第二个参数)。 让我们将函数绑定到名称并使用它。
# let addThree = add(3);
# addThree(2);
- : int = 5;
链接功能
简洁易读的代码是高质量和可维护性的重要方面。 反向应用运算符|
使我们可以将函数链接在一起,而无需创建中间的let
绑定或复杂的嵌套。
我将创建一个函数列表,将它们一起使用时,将使用字符串,将其大写,然后将其转换为小写元素。
# let info = String.capitalize(String.lowercase("ALERT"));
使用反向应用程序运算符|
,我们可以改为:
# let info = "ALERT" |> String.lowercase |> String.capitalize;
结合参数的部分应用,此语法可能非常有用。
# [8, 3, 6, 1]
|> List.sort(compare)
|> List.rev
|> List.find(x => x < 4);
- : int = 3
递归函数
函数递归要求let
绑定必须在其自己的本地范围内才能访问。 使用rec
关键字,我们可以启用此可见性以声明递归函数。
我正在创建一个函数,该函数最多可以计数10个,并且每次递增后都会带出参数。
# let countUntilTen = x => {
if (x < 10) {
print_int(x)
countUntilTen(x + 1);
};
};
Error: Unbound value countUntilTen
我们收到此错误的原因是函数主体无法访问该函数指向的let
绑定。 但是包含rec
关键字可以实现。 这将使函数能够查看并调用自身,从而为我们提供递归的功能。
# let rec countUntilTen = x => {
if (x < 10) {
print_int(x)
countUntilTen(x + 1);
};
};
let countUntilTen: int => unit = <fun>;
# countUntilTen(6);
6789- : unit = ()
如果要创建相互递归的函数,可以通过使用rec
关键字从单个递归函数开始,然后使用and
关键字添加第二个递归函数来实现。
# let rec add = x => ! even(x)
and even = x => {
if(x == 0) {
true;
} else {
odd(x - 1);
};
};
let odd: int => bool = <fun>;
let even: int => bool = <fun>;
元组,列表和数组
元组
元组是一种数据结构,它允许我们存储固定数量的任何类型的值。
# ("Anna", 24);
- : (string, int) = ("Anna", 24)
每个元素都是通过其位置而不是其名称来标识的。 我们还可以嵌套一个元组。
# type point = (int, int);
# let myPoint: point = (4, 3);
要访问元组的元素,Reason为我们提供了两个方便的功能来访问前两个元素。 fst
将访问第一项,而snd
将访问第二项。
# fst((0,2));
- : int = 0
# snd((0,2));
- : int = 2
让我们看一下另一个元组:
# let (a,b,c) = ("Rajat","Writer",23);
let a: string = "Rajat";
let b: string = "Writer";
let c: int = 23;
在这里,如果您不想为每个位置分配一个值,则可以简单地用_
替换该值。
每个元组都是不可变的。 因此,我们无法更新元组,只能创建新的元组。
清单
与元组不同,列表是同质且不可变的。 因此,我们不能在列表中存储不同类型的项目。
# let List = ["Rajat", "Writer"];
而且,Reason允许我们使用变体来保留不同的类型并创建标签列表。
# type rajat = Name(string) | Age(int);
# [Name("Rajat"), Age(23)];
作为不可变的,我们可以在List中append
, prepend
或替换项。
# List.append(["Rajat"],["Writer"]);
- : list(string) = ["Rajat", "Writer"]
# ["Rajat"] @ ["Writer"]
- : list(string) = ["Rajat", "Writer"]
要将一个或多个元素添加到列表的前面,请使用...
运算符。 请注意, ...
只能用于前置元素。
# [0, ...[1, 2, 3]];
- : list = [0, 1, 2, 3]
要访问列表,您需要使用switch
表达式。
# let list = ["Rajat", "Writer"];
# let message =
switch(list) {
| [] => "There is no data"
| [head, ...rest] => "My name is " ++ head
};
let message: string = "My name is Rajat";
这会变得非常复杂。 我们可以改用List.nth
来访问列表中的元素。
# List.nth([2, 3], 0);
- : int = 2
但是同样,如果您的列表太小,此方法可能会引发异常。
相反,我们可以使用List.map
来操纵List中的每个项目。 List.map
也可以接受一个函数作为其第一个参数,并接受本地List作为第二个参数。
# List.map(x => x * 5, [2, 4]);
- : list(int) = [10, 20]
原因中,我们还有其他功能,例如find
, filter
, sort
, exist
, split
以帮助List进行更多操作。
数组
数组与列表相似,不同之处在于数组的内容包装在管道和方括号中,并且与列表不同,数组是可变的。
# let array = [|2, 3|];
# array[0];
- : int = 2
# let array[0] = 3;
平等
在任何编程语言中,平等通常有两种类型:
- 结构相等 -比较两个值是否具有相同的内容。
- 引用相等 -检查两个值是否指向内存中的相同表示形式。
结构平等
结构相等用于比较数据结构,布尔值,元组,列表和记录。
# "Rajat" == "Rajat";
- : bool = true
# true == false;
- : bool = false;
在Reason中,我们可以使用结构相等性甚至比较嵌套结构,还可以检查不平等性,如下所示:
# (false, [{name: "Rajat", job: "writer"}]) != (true, [(name: "Rajat", job: "writer"}]);
- : bool = false
参考平等
与结构相等不同,引用相等不比较值中存储的内容。 而是检查值是否指向内存中的相同表示。
# let rajat = {name: "Rajat", job: "Writer"};
# rajat === rajat;
- : bool = true
# rajat === {name: "Rajat", job: "Writer"};
- : bool false
在少数情况下,您将可以使用引用相等。 尽管这是一项非常快速的检查,但如果传入具有相同内容的值,也可能导致我们的应用程序丢失。引用比较两个整数始终会完全实现它们的实现方式。
# 24 === 24;
- : bool = true
未完待续…
到此结束本系列第 3 部分的第2部分 。 第3部分即将推出,请紧紧进行:)
这是第1部分 ,如果您还没有阅读的话。
我是GeekyAnts的技术内容作家RajatS 。 有抱负的编码员,任重道远。 热爱漫威电影的死硬DC漫画迷。 以多任务处理而闻名。
感谢您的阅读,希望对您有所帮助! 如果您喜欢此帖子,请and,然后在此处和/或在Twitter上关注我,以随时了解我的新帖子!
From: https://hackernoon.com/get-reason-able-with-reasonml-part-2-65d3ab851570