Scala函数式编程(五) 函数式的错误处理

前情提要

Scala函数式编程指南(一) 函数式思想介绍

scala函数式编程(二) scala基础语法介绍

Scala函数式编程(三) scala集合和函数

Scala函数式编程(四)函数式的数据结构 上

Scala函数式编程(四)函数式的数据结构 下

1.面向对象的错误处理

在介绍scala的函数式的错误处理之前,我们要先来介绍一下其他情况下的错误处理方式。

以java为例,常见的错误处理方式不外乎两种,一种是及时捕捉到异常,然后当场进行处理。

try{
    ...
}catch(Exception e){
    ...

}finally{

}

另一种则是将异常抛出,层层捕获,然后在最上层对异常进行统一处理,这种通常是在大型项目的时候会使用。

这两种错误处理的方法是,在我们日常的编程中,已经足以应对多种情况。

但在函数式编程中却不行,函数式编程追求的是无副作用的代码,无副作用最直接的应用就是可以放心得并发运行,而抛出异常却会产生副作用。

try catch处理的弊端,在并发编程中其实有较为明显的体现。

以spark为例,如果spark主节点master询问worker节点的健康情况,当worker节点出现异常时,显然让master节点来捕获并处理这个异常,有点不符合情理。

更合理的处理,应该是让master接收到一个表示错误情况的消息,然后再决定接下来如何处理。而worker的异常就让worker自己去解决吧。

而在scala中,有一种特定的类型,它用来表示可能导致异常的一个计算过程,这就是Try。

2.从Option到Try

前面有介绍过Option,相关介绍可以看这里Scala函数式编程(三) scala集合和函数。

这里简单介绍一下Option。

Option呢,其实就是薛定谔的值,里面可能有值,也可能没有值。只有到要看的时候,才会知道Option里面到底有没有值。

Option全程叫Option[A],表示Option里面存的是A类型的值,这个A可以是Int,String,等等。我们可以通过get这个api来获取Option[A]里面的值,当不存在时,get会返回None。

可以通过isEmpty,来确认Option里面到底是不是有值。也可以通过getOrElse来指定没有值的时候要返回什么值。

Try[A]和Option类似,都是表示一个可能有也可能没有的东西。实际对应过来, Try[A]就表示一个可能成功也可以失败的计算,如果成功,则返回A类型,如果失败,则返回Throwable。

先最在交互式环境中直观看一下怎么使用吧:

scala> import scala.util.Try
import scala.util.Try

scala> Try(1+1)
res15: scala.util.Try[Int] = Success(2)

scala> Try(1/0)
res16: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)

能够实现这个功能,主要是因为Try的两个子类型:

  • Success[A]:代表成功的计算。
  • 封装了 Throwable 的 Failure[A]:代表出了错的计算。

是不是和Option很像呢?也是薛定谔的错误,在没打开来看之前,Try里面可能是成功的,也可能是失败的。

同样可以通过isSuccess和isFailure来确认到底这个Try是成功还是失败。

如果一个函数中有一个计算可能会出错,那么我们就可以直让函数返回Try,然后对成功还是错误,就全交由调用者来进行处理,比如上面说到的,Spark的那个例子。

3.Try的使用

上面初步介绍了Try的含义和用法,接下来就来看看Try这个东西,还有哪些常规的用法吧。

3.1 map

map是scala里面非常常用的一种操作,Try里面也有!

对Try使用Map的话,会将一个是Success[A]的Try[A]映射到Try[B]会得到Success[B]。如果它是Failure[A],就会得到Failure[B],而且包含的异常和Failure[A]一样。

看看例子吧:

//新建一个Try,注意,这里是Try[Int]
scala> val tryMap = Try(1+1)
tryMap: scala.util.Try[Int] = Success(2)

//使用Map,让它变成Try[String]了
scala> tryMap.map(_.toString)
res46: scala.util.Try[String] = Success(2)

//新建一个会失败的Try[Int]
scala> val tryMapFail = Try(1 / 0)
tryMapFail: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)

//转换成Try[String]了,但Failure的异常类型不变
scala> tryMapFail.map(_.toString)
res47: scala.util.Try[String] = Failure(java.lang.ArithmeticException: / by zero)

Try不止支持map,还支持for,flatMap,filter等常规操作,从这个角度看,Try反而更像一种数据结构。

3.2 错误时候的默认值getOrElse

和Option一样,Try还很方便得提供了getOrElse这个方法。当你想为失败的时候做些什么的时候就可以用这个api。

这个我举个简单的例子,将字符串转换为Int类型。在字符串转Int类型的时候呢,可能会遇到一些不符合规范的数据。这时候你就不得不考虑数据是否可以安全得转换成Int,但有了Try,可以很方便得用getOrElse,方法。

当遇到不能转成Int的字符串,给与一个默认值即可。

scala> import scala.util.Try
import scala.util.Try

scala> "12".toInt
res17: Int = 12

scala> "asd".toInt
java.lang.NumberFormatException: For input string: "asd"
  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
  at java.lang.Integer.parseInt(Integer.java:580)
  at java.lang.Integer.parseInt(Integer.java:615)
  at scala.collection.immutable.StringLike$class.toInt(StringLike.scala:272)
  at scala.collection.immutable.StringOps.toInt(StringOps.scala:29)
  ... 32 elided

scala> Try("asd".toInt).getOrElse(-1)
res19: Int = -1

但这里还是得多说一句,这种做法会忽略掉原本应该抛出的错误,你需要明确知道自己确实是要忽略掉这个错误才能这样用

否则可能因为设置的默认值导致出现问题,而毫无头绪,因为程序并没有报任何错误!!

3.3 模式匹配

我们可以不必如java的try catch那般去处理Try失败时返回的异常。因为我们有scala的模式匹配。

不得不说,模式匹配真的是很强大的一个语言特性。前面不是说到嘛,Try有两个子类,Success和Failure,成功时候返回Success,失败时返回Failure。

所以我们就能够这样做:

import scala.util.Success
import scala.util.Failure
val operation = Try(1 / 0)
operation match {
  case Success(num) => println(num)
  case Failure(ex) => println(s"Problem is ${ex.getMessage}")
}

因为除数为0,所以这个Try是失败的,所以这里会输出:Problem is / by zero

scala强大的模式匹配,可以方便得让我们处理错误和非错误的情况。

4. 小结

Scala 的错误处理和其他范式的编程语言有很大的不同。 Try 类型可以让你将可能会出错的计算封装在一个容器里,并优雅的去处理计算得到的值。 并且可以像操作集合和 Option 那样统一的去操作 Try。

同时Try[A]也支持常见数据结构中的操作,诸如Map,Filter等常规的api都支持。

Try这种错误处理的方式,明显更适用于函数式的情况,也就是说更适合在并发编程的时候使用。

但在我看来,Try也是有一些不好的地方,比如说在代码可读性方面就比try catch这种方式差。不得不说,虽然写起来比较啰嗦,但看着这个结构确实是一目了然。

但是不管如何,在我看来,函数式的错误处理依旧是很有趣的一个东西。如果合适的话,可以多在代码中尝试去使用:)

以上~

原文地址:https://www.cnblogs.com/listenfwind/p/12337437.html

时间: 2025-01-12 00:39:05

Scala函数式编程(五) 函数式的错误处理的相关文章

[原创译书] JS函数式编程 2.函数式编程基础

2 函数式编程基础 ?? Functional Programming in Javascript 主目录上一章 Javascript函数式编程的力量——举个例子 现在,你已经稍稍领略了一点函数式编程能做的事情.但是到底什么是函数式编程呢? 如何来区分一个语言是否是函数式的?又如何来区分一段程序是否是函数式的呢? 在这章,我们先来看看下面的问题,这些问题覆盖了函数式编程的核心概念: 使用函数和数组实现控制流 编写纯函数.匿名函数.递归函数等等 像对象那样传递函数 利用map().filter()

我是如何开始去了解Python函数式编程--Python函数式编程初涉

Python函数式编程 1. 开始我们会了解什么是函数式编程: 函数:function 函数式:functional,一种编程范式 函数式编程特点:把计算视为函数而非指令,贴近计算 纯函数式编程:不需要变量,没有副作用,测试简单,支持高阶函数,代码简洁 Python支持的函数式编程特点: 不是纯函数式编程:允许有变量 支持高阶函数:函数也可以作为变量传入 支持闭包:有了闭包就能返回函数 有限度的支持匿名函数 2. 高阶函数 变量可以指向函数,函数名其实就是指向函数的变量,而高阶函数其实就是可以接

初识函数式编程与函数式接口(一)

目前大部分的 JAVA8 的教程一上来就给大家将 Lambda 表达式,方法引用,给大家搞得云里雾里,最终导致 JAVA8 学习的不是特别透彻.我们先来了解一下什么时候能用 Lambda 表达式,然后在探究怎么用 Lambda 表达式. 从函数式编程开始 前一章节我们说过,JAVA8 其实是 Java 像其他语言或者一些优秀的框架学习的结果.函数式编程这个概念提出的非常早,有很多语言都是支持函数式编程的.JAVA8 中也对函数式编程做了支持.我们下面要介绍的函数式接口等概念都是围绕函数式编程而生

scala函数式编程(二) scala基础语法介绍

上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些java或者python的基础,毕竟大部分人不会将scala当作第一门学习编程的语言. 不过这些语法知识记不住也没关系,本身语法这种东西就应该在使用中被记住.这里写这篇的目的也只是梳理一遍,方便大家对语法有个初步的印象,后面可以随时查询. PS:所使用的版本是scala 2.11.8,那我们开始吧 一.

(转)现代C++函数式编程

本文转自:http://geek.csdn.net/news/detail/96636 现代C++函数式编程 C++ 函数式编程 pipeline 开发经验 柯里化 阅读2127 作者简介: 祁宇,武汉烽火云创软件技术有限公司研发中心技术总监,<深入应用C++11>作者,C++开源社区purecpp.org创始人,致力于C++11的应用.研究和推广.乐于研究和分享技术,爱好C++,爱好开源. 导读: 本文作者从介绍函数式编程的概念入手,分析了函数式编程的表现形式和特性,最终通过现代C++的新特

javascript 函数式编程

编程范式 编程范式是一个由思考问题以及实现问题愿景的工具组成的框架.很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等. 函数式编程范式 函数式编程就像一辆氢燃料驱动的汽车——先进的未来派,但是还没有被广泛推广.与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态.函数式编程将计算转化作表达式求值.这些表达式全由纯数学函数组成,这些数学函数都是一流的(可以被当做一般值来运用和处理),并且没有副作用. 函数式编程

Python核心编程读笔 9:函数和函数式编程

第11章 函数和函数式编程 一 调用函数 1 关键字参数 def foo(x): foo_suite # presumably does some processing with 'x' 标准调用 foo(): foo(42)  foo('bar')  foo(y) 关键字调用 foo(): foo(x=42)  foo(x='bar')  foo(x=y) 即明确给出相应的参数名 2 参数组 Python允许程序员执行一个没有显式定义参数的函数,相应的方法是通过一个把元组(非关键字参数)或字典

编程的97件事——2、应用函数式编程原则

Apply Functional Programming Principles Functional programming has recently enjoyed renewed interest from the mainstream programming community. Part of the reason is because emergent properties of the functional paradigm are well positioned to addres

MapReduce分布编程模型之函数式编程范式

导读: 计算机科学是算法与算法变换的科学,算法是计算机科学的基石. 任何一个计算问题的分析与建模,几乎都可以归为算法问题. MapReduce算法模型是由Google公司针对大规模群组中的海量数据处理而提出的分布编程模型,主要应用于大规模数据集{大于1TB}的分布并行运算. 在MapReduce模型中的Map{映射}和Reduce{化简}创意来自函数型编程语言,同是也继承了向量型编程语言的特性. MapReduce模型能够使程序员在不了解分布式并行编程的情况下,将自己书写的程序在分布式系统上运行

什么是函数式编程

导读 建议先阅读一下这几篇博客: 函数式编程初探 函数式编程入门教程 图解 Monad 什么是函数式编程 函数式编程中的函数指的并不是编程语言中的函数(或方法),它指的是数学意义上的函数,即映射关系(如:y = f(x)),就是 y 和 x 的对应关系. 数学上对于函数的定义是这样的:"给定一个数集 A,假设其中的元素为 x.现对 A 中的元素 x 施加对应法则 f,记作 f(x),得到另一数集 B.假设 B 中的元素为 y." 所以当我们在讨论"函数式"时,我们其