使用ReasonML变得合理-第2部分

非法状态,选项类型,函数,元组,列表,数组和相等性

这是一个分为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"

但是仍然有很多地方可能出问题。 例如,如果有人将loadingerror都设置为true怎么办?

if/else表达式呈现loading ,但是根据定义,它永远不会发生。 由于数据的结构方式,这提出了一个问题,在这种情况下,我们的UI代码应该做什么? 也许显示error而不是loading

我们可以使用变体来解决这个问题。 我将使用变体重新创建类型请求。 每个标记代表状态之一,即loadingerrorsuccess 。 成功是相同的附加条件。

# 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变量,我们将永远无法同时具有loadingerrorsuccess

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构造函数将包含另一个数据结构,例如RecordList类的另一个变体。

# 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中appendprepend或替换项。

# 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]

原因中,我们还有其他功能,例如findfiltersortexistsplit以帮助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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值