【从入门到放弃】Scala

分享 未结
1 1 1 322
小编 2020-05-17发布
收藏 点赞
来源: https://zhuanlan.zhihu.com/p/136331336

先事先声明一下,“从入门到放弃”是一个系列。这个系列是我今年的一个小实验:用闲暇时间学习一个技术,学它一个月,然后写一个感受,下个月再换。与其说是“放弃”,不如说是“不恋战”。毕竟学的断断续续,并没有真正入门,也没有真正放弃。

对于Scala,我之前虽然没有系统学过,但是工作中用到过两次。

第一次是在雅虎工作的时候。我们当时发现,雅虎新闻的评论区经常有垃圾信息。一个新闻,底下一群人喊口号、或者互相言语攻击:MAGA啊、libtard啊、trumptard啊、snowflake之类的。而且这些没有价值的评论经常被一群人(或者僵尸号)点赞。如果按照点赞数量排序的话,这些垃圾评论就会顶到最高。

为了解决这个问题,我们当时想在评论排序上引入相关性。如果评论和文章相关,那相关性就高。如果把相关性和点赞数量相结合,评论区至少不会太恶心。

怎么算出相关性呢?我们先要做一个评论相关性的数学模型,从新闻文本里面抽取一些主题,然后每个评论抽取一些主题,如果主题贴近,这就说明评论和新闻相关。“高相关性”的评论就会被顶到高处,而低相关性的评论会放到靠后的位置。

当时这个数学模型是一个实习生写的,写的时候用的是Scala。实习生写完以后就上学去了,没有人接管这个项目。我当时正在做评论区优化,于是就把它接了下来。刚刚接手的时候我真的是完全不懂Scala,简单学了一晚上,第二天就跟赶鸭子上架似的写代码去了。经过一番折腾,我终于磕磕绊绊地把这个数学模型写成了一个Web API,应用到了评论区。

这是今天的雅虎主页头条新闻的评论区,基本上没有喊口号了,也没有互相人身攻击了

第二次接触Scala是在Stripe工作的时候。Stripe是个做API的公司。它的API的权限管理主要是用API key做的。我的任务就是找到所有存储系统的API key,把它们划掉。

Stripe的API流量很大,每秒成千上万的RPS。Stripe成立于2009年,我在2019年开始清除数据,那么就有十年的数据。

10年 == 315,569,520秒

假设每秒request有1000个,那么一共有315,569,520,000条数据需要清除。三千多亿条数据,一个脚本是干不过来的。于是我借助了一个开源工具Summingbird,它可以用分布式的方法做数据处理。而这个工具只支持Java和Scala。

这两个项目如今都已经做完了,但我总觉得对Scala不甚了解。于是这个月初我开始系统地按照官方教程把它学了一遍。

官方教程链接:

Introduction

我为了更好地理解官方教程,我把一些教程的例子拷贝下来自己把玩:

quzhi1/ScalaPlayground

学完Scala以后我的第一感受就是它是个Java和Scripting language结合的产物。Java是一个很古典的语言,静态类型和面向对象这些设计真的很经典。然而Java仍有一些需要改进的地方。

第一个地方就是满足不了今天才出现的奇怪需求。最典型的就是Lambda。Java发明的时候不太需要考虑如何把一个function当做value用的情况,而今天这种情况特别多。另一个需求就是stream programing。在Java 8以前,Java本身是做不了stream programming的。Java 8虽然支持了这个功能,但是还是很难用。大家还是该用for loop用for loop,旧习难改。这两个需求Scala都满足了。

第二个地方就是Java很危险。我说的危险不是它容易漏东西,而是说Java初学者容易把Java写出错。我刚刚接触Java的时候,继承的逻辑都是混乱的,随意声明class field,胡乱写Generic。本来可以compile time捕捉的bug,最后在run time才能看到。这些给程序员的坑Scala都尽量避免了。

在我看来,Scala就是一个威力加强版的Java。

我喜欢Scala很多设计:

第一个就是case class。它解决了Java里面clone的问题。Java容易分不清楚我和clone之间的关系。我的双胞胎到底是不是我自己呢?Java为了解决这个问题,不得不引入了一个Comparable interface和equals method。而Scala就没这么多顾虑。

第二个是object。Java的一些class需要保证singleton,而singleton在Java里面写起来很别扭,很多人干脆就不写了。Scala的object很好写,这方面比Java安全得多。

第三个是generic。在我见过的语言里,只有Scala把generic做到了极致。function可以有generic,class可以generic,class继承的时候也可以generic。而且generic可以定义上下限,定义继承关系,只要想不到没有做不到。

当然Scala也有一些我不太不习惯的设计。

有些名字我觉得起的别扭。比如Unit,英语应该是单位的意思,结果在Scala里面它代表void。还有一个例子:Nil。Nil在英语里是不存在的意思,所以很多语言nil就是null。结果Scala里面nil不是null,而是个空集合(empty collection)。这些命名方式真的很令人困惑。

Scala有implicit parameter和implicit value。这个设计我觉得特别像C和C++时代的global variable,感觉特别危险。不过这方面我的理解能力有限,它这么设计或许有一些特别的原因。

感受就是这么多,下面是我的学习笔记:

Basics

  • Like JavaScript, val can not be re-assigned, var can.Methods is "functions of other languages"Functions is basically anonymous functionsUnit is basically voidInstances of case classes are immutable, and they are compared by value (unlike classes, whose instances are compared by reference).You can instantiate object without using classTraits are basically interfaces

Types

  • Unit is a value type which carries no meaningful information. There is exactly one instance of Unit which can be declared literally like so: ().If Scala is used in the context of a Java runtime environment, AnyRef corresponds to java.lang.Object.

  • Nothing is a subtype of all types, also called the bottom type. There is no value that has type Nothing.Null is a subtype of all reference types (i.e. any subtype of AnyRef). It has a single value identified by the keyword literal null.Nil is empty collection. So Nil != nullImplicit conversion is very useful for Java <-> Scala type conversionGeneric method and generic class can infer type from value

Classes, tuples and traits

  • Traits == Java interfaceFunction can return tuple. Tuple type is TupleN (N can be 1-22)You can compose a class use class D extends B with C, where B is concrete class, and C is a trait. In this case, C is usually an optional trait you can plugin.Case class is a very good way to do immutable classTraits and classes can be marked sealed which means all subtypes must be declared in the same file.An object with the same name as a class is called a companion object. Conversely, the class is the object’s companion class.

Function and methods

  • Function & method can both be directly passed into another function as lambda. Just like go.Function can also be return type as lambdaYou can define private helper function inside a functionThere can be implicit parameters. If you don't give it,Scala will first look for implicit definitions and implicit parameters that can be accessed directly (without a prefix) at the point the method with the implicit parameter block is called.Then it looks for members marked implicit in all the companion objects associated with the implicit candidate type.Method can have generics too.Just like Ruby, operators (like + - * /) are also methods. So you can define them for your type.Use by-name-parameters to force re-evaluate method parameter

Object

  • Can do singleton classCan do static class methodCan do factory method.apply construct an object; .unapply extract an objectThe return type of an unapply should be chosen as follows:If it is just a test, return a Boolean. For instance case even().If it returns a single sub-value of type T, return an Option[T].If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].
  • If you want to share an object across package, use package object

Classes relationship

  • This article explains how scala can delete variance fault in compile time: Scala与Java之间的型变对比 - 映柳枫鹏的文章 - 知乎 and scala 逆变有什么用? - 夏梓耀的回答 - 知乎.
  • Variances is used for defining relationship between super-types. If A < B, then it defines the relations of List[A] and List[B].
  • Upper type bound makes generic super class possible, e.g. List[T].
  • Lower type bound makes generic child class possible. It usually works with covariant type.
  • Suppose class B has an inner class A, class C also has an inner class A. In Java, B.A == C.A. In scala, B.A == C.A.
  • trait and abstract class can have generic too.
  • In Java, A implements B, C. In scala, A with B with C (Since you can't do A extends B, C).
  • Trait can access other trait's member using self type

Annotation

  • Like Java, scala accept annotation
  • Scala can optimize for tail-recursion.
  • Just add @tailrec. (About tail recursion, see 什么是尾递归? - 知乎)
  • Just like C++, you can make method inline. Just add @inline
  • All Java annotations can be used in Scala


回帖
  • 2020-05-23

    啦进群里教导教导

    0