Tags:编程
Scala用一种简洁的高级语言将面向对象和函数式编程结合在一起。Scala的静态类型有助于避免复杂应用程序中的错误,其JVM和JavaScript运行时使您可以轻松访问庞大的库生态系统来构建高性能系统。
软件功能:
无缝Java互操作
Scala运行在JVM上,因此Java和Scala堆栈可以自由混合,实现完全无缝的集成。
类型推断
所以类型系统感觉不那么静态。不要为类型系统工作。让类型系统为您工作!
并发与分发
对集合使用数据并行操作,对并发和分发使用actors,或者对异步编程使用future。
特点
结合Java风格接口的灵活性和类的强大功能。想想有原则的多重继承。
模式匹配
想想类固醇的“开关”。与类层次结构、序列等匹配。
高阶函数
函数是一级对象。以保证类型安全的方式组合它们。把它们用在任何地方,传递给任何人。
使用方法:
使用Scala实现文件的拷贝
读取行:要读取文件的所有行,可以调用scala.io.Source对象的getLines方法:也可以对getLines应用toArray或toBuffer方法。
将这些行放到数组或缓冲当中,将文件内容读成一个字符串:val lines = source.mkString。
读取字符:要从文件中读取字符,可以直接把Source对象当做迭代器:如果想查看某个字符,但是不处理掉的话,调用source对象的buffered方法。
读取词法单元或数字:通过split方法对转化成行的文件内容进行划分,通过toInt或toDouble方法把字符转化成整数或浮点数。
写入文本:Scala没有內建的对写入文件的支持,要写入文本文件,可以使用java.io.PrintWriter.
值得一提的是FileChannel在使用前,必须要打开。需要通过InputStream/OutputStream/RandomAccessFile获取,BufferedReader/BufferedWriter获取不到。
在SCALA 3中导入建议新功能:
隐式允许编译器为您“编写”程序的重要部分。例如,编译器可以召唤JSON序列化器和反序列化器以获取完整的类型层次结构。
但是,使用隐式操作可能会很困难。值得庆幸的是,斯卡拉3编译器极大地提高了在缺少implicits,使其更容易看到的情况下显示的错误信息的质量,其中一个隐含参数不能由编译器,并推断如何来解决这个问题。
本文在具体的代码示例中展示了这些实际的改进。
动机
在2018年Scala开发人员调查中,“隐式”一词出现在“学习Scala时,您面临的最大挑战是什么?”问题中。
我们还看到,在2019年开发人员调查中,有35%的受访者表示,处理隐式隐式缺失是他们日常工作流程中的主要痛点。此外,他们表示,使用隐式函数时,他们遇到的两个最痛苦的问题是“查找已推断出的参数”和“修复“隐式找不到”错误”。最后但并非最不重要的一点是,受访者最常提到的与隐性相关的其他痛苦点是“进口”。
几个月前,杰米·汤普森(Jamie Thompson)与社区进行了讨论,以更好地理解问题。我们发现“条件”隐式可能与大多数问题有关。条件隐式是隐式定义,它们本身具有隐式参数。例如,一个隐式Ordering[List[A]]实例需要一个隐式 Ordering[A]实例:
implicit def orderingList[A](implicit orderingA: Ordering[A]): Ordering[List[A]]
考虑一下,当您调用需要隐式方法的方法时会发生什么 Ordering[List[Int]]。编译器将搜索此类隐式定义,并发现orderingList如果存在type的隐式实例,则该隐式定义可能是一个不错的选择Ordering[Int]。编译器搜索这样的隐式定义(它在Ordering伴随对象中找到 ),并Ordering[List[Int]] 通过将Ordering[Int]实例提供给隐式定义来召唤初始隐式参数orderingList。在此示例中,我们仅涉及两个隐式定义,但是在实践中,条件式隐式定义可以形成更长的链。
现在,让我们看看如果链中某处发生故障,Scala 2中会发生什么。例如,当我们调用需要隐式 Ordering[List[Foo]]但没有隐式Ordering[Foo]实例的方法时:
class FooList(List(new Foo)).sorted
Scala 2编译器产生以下错误:
No implicit Ordering defined for List[Foo].
错误消息说找不到Ordering类型的隐式实例 List[Foo]。但是,此消息不是很准确。失败的实际原因是Orderingtype 没有隐式 实例Foo。因此,编译器无法调用Ordering 类型的隐式实例List[Foo]。
这是我们发现的第一个具体问题:错误消息并不准确知道哪里链是缺隐。
我们确定了第二个问题是有关implicits问题往往由于缺少进口,但要找到什么来进口是很难的。
下一节将展示Scala 3如何通过提供更详细的错误消息和可行的反馈来解决这两个问题。
显示问题出在哪里
如果在隐式定义链中找不到隐式参数,Scala 3编译器现在会显示它可以构建的完整链,直到找不到参数为止。这是一个模仿上述Ordering[List[A]]问题的示例:
// `Order` type class definition, similar to the `Ordering` type class of// the standard librarytrait Order[A] {
def compare(a1: A, a2: A): Int}object Order {
// Provides an implicit instance of type `Order[List[A]]` under the condition
// that there is an implicit instance of type `Order[A]`
implicit def orderList[A](implicit orderA: Order[A]): Order[List[A]] = ???}// Sorts a `list` of elements of type `A` with their implicit `order` relationdef sort[A](list: List[A])(implicit order: Order[A]): List[A] = ???// A class `Foo`class Foo// Let’s try to sort a `List[List[Foo]]`sort(List(List(new Foo)))
Scala 3编译器给出以下错误消息:
Error:| sort(List(List(new Foo)))| ^|no implicit argument of type Order[List[Foo]] was found for parameter order of method sort.|I found:|| Order.orderList[A](/* missing */implicitly[Order[Foo]])||But no implicit values were found that match type Order[Foo].
错误消息现在显示了编译器通过链接隐式定义而走了多远,以及由于找不到隐式参数而最终停止在哪里。在我们的例子中,我们看到编译器尝试了定义,orderList但是没有找到一个隐式Order[Foo]。因此,我们知道要解决此问题,我们需要实现一个隐式Order[Foo]。
记录下来,显示完整的隐式链的想法是Torsten Schmits在splain编译器插件中提出的,该插件可在Scala 2中使用。
建议如何解决问题
如果缺少的隐式参数定义在某个地方但需要导入,则Scala 3编译器会向您建议import可以解决该问题的子句。
这是说明此的示例:
// A class `Bar`class Bar// An implicit `Order[Bar]`// (note that it is _not_ in the `Bar` companion object)object Implicits {
implicit def orderBar: Order[Bar] = ???}// Let’s try to sort a `List[Bar]`sort(List(new Bar))
编译器产生以下错误:
Error:| sort(List(new Bar))| ^|no implicit argument of type Order[Bar] was found for parameter order of method sort||The following import might fix the problem:|| import Implicits.orderBar
Scala 3编译器不仅仅是报告未找到隐式参数,还寻找可能提供缺少参数的隐式定义。在我们的情况下,编译器建议使用import Implicits.orderBar,它确实可以修复编译错误。
一个更复杂的例子
一个典型的例子是猫traverse库的操作。该操作被定义为 存在隐式实例的任何类型的条件扩展方法。该操作采用一个函数和一个类型为隐式的参数。F[A]Traverse[F]A => G[B]Applicative[G]
实际上,这种非常通用的操作用于各种特定的上下文中。例如,将验证结果列表转换为包含列表的单个验证结果,或将可选的异步结果转换为异步的可选结果。但是,由于它是一种条件扩展方法,并且由于它采用了隐式参数,因此很难找到正确的导入使其起作用。
您无需熟悉类型类Traverse,也无需Applicative了解本文的其余部分。关于该操作,只有两件事要了解traverse:
List[A]如果存在类型的隐式实例Traverse[List](它是一个条件扩展方法),则它可用于类型的值,
操作本身采用类型为的隐式参数Applicative。
可以使用扩展方法在Scala 3中对此进行建模:
// The `Traverse` type class, which provides a `traverse` operation as an extension methodtrait Traverse[F[_]] {
def [G[_], A, B](fa: F[A]).traverse(f: A => G[B])(implicit applicative: Applicative[G]): G[B]}// The applicative type class (its actual definition does not matter for the example)trait Applicative[F[_]]
假设在对象中定义了类型的给定实例和类型Traverse[List]的给定实例(给定的实例是在Scala 3中定义隐式实例的新方法):Applicative[Option]Givens
object Givens {
given traverseList as Traverse[List] = ???
given applicativeOption as Applicative[Option] = ???}
现在我们已经设置了上下文,让我们来看一个使用的具体示例traverse。
首先,考虑一个函数parseUser,该函数User 从中解析a String(例如,包含JSON对象):
def parseUser(string: String): Option[User]
函数的返回类型为Option[User],可以表示的解析失败None或的解析成功Some。
我们可以使用操作traverse和函数parseUser(解析一个用户)来实现一个函数parseUsers,该函数解析一个用户列表。该函数的签名如下:
def parseUsers(strings: List[String]): Option[List[User]]
同样,结果类型是Option[List[User]]可以表示解析失败的结果(None如果任何字符串解析失败,则返回)。
该功能可以实现如下:
def parseUsers(strings: List[String]): Option[List[User]] =
strings.traverse(parseUser)
但是,如果尝试使用Scala 2编译此代码,则会出现以下错误:
value traverse is not a member of List[String]did you mean reverse?
该错误消息无助于找到解决方案。
另一方面,使用Scala 3进行编译可以提供更好的帮助:
[E008] Not Found Error:| strings.traverse(parseUser)| ^^^^^^^^^^^^^^^^|value traverse is not a member of List[String], but could be made available as an extension method.||The following import might make progress towards fixing the problem:|| import Givens.traverseList
让我们应用建议并导入Givens.traverseList。现在,编译器提供以下错误:
Error:| strings.traverse(parseUser)| ^|no implicit argument of type Applicative[Option] was found for parameter applicative of method traverse in trait Traverse||The following import might fix the problem:|| import Givens.applicativeOption
如果我们应用新建议(导入Givens.applicativeOption),我们的程序将编译!
Scala 3编译器首先建议使用import Givens.traverseList,以便扩展方法traverse可用。然后,它建议使用import Givens.applicativeOption,这是调用该traverse 操作所必需的。
摘要
在Scala 2中处理“隐式找不到”错误可能很困难,尤其是因为开发人员无法准确看到在隐式定义链中找不到哪个隐式参数,或者因为他们不知道需要什么导入添加到他们的程序中。
Scala 3通过以下方法解决了这两个痛点:
提供更精确的错误消息,准确显示在隐式定义链中找不到哪个隐式参数,
提供可行的反馈,建议import可能提供缺少的隐式内容的条款。
您已经可以在Dotty 0.24.0-RC1中尝试此功能。
安装方法:
下载Scala官方版的压缩包,解压后,双击msi文件,进入安装界面,点击next
查看软件协议,选择i accept...,点击next
设置软件安装位置,点击browse可以自由设置,建议大家选择安装在D盘,然后点击next
确认安装信息,点击install
Scala官方版正在安装,我们耐心等待
软件安装成功,点击finish
接下来需要配置Scala的环境变量,需要提醒一下在安装Scala之前需要安装jdk,并且配置JDK的环境变量。我们看一下本地安装完成后的目录,如下图所示。
最后我们配置Scala的环境变量,这台电脑-->右键“属性”-->高级系统设置-->环境变量,我们选择Path环境变量,并点击“编辑”按钮,我们将上图看到的Scala安装目录下的bean目录配置到Path环境变量中即可。
安装完成后我们需要检验是否安装成功,Win+R打开命令行,输入 scala -version,若出现Scala的版本信息则说明安装成功,如下图所示。
软件评论 请自觉遵守互联网相关政策法规,评论内容只代表网友观点,与本站立场无关!
网友评论