[PLT] Programming Lanaguage Features

Scala 2.8

breakOut

看如下一段代码:

val sampleDict : Map[String,String] = param.fields.map(x => (x.field_name, x))
//错误,type mismatch 
val sampleDict : Map[String,String] = param.fields.map(x => (x.field_name, x))(breakOut)
//正确

breakOut在这个过程中提供了implicit bf : CanBuildFrom[Repr, B, That]
参数推断过程如下:

val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)

sealed abstract class List[+A] 
extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]]
// A = String Repr = List[String]

trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] 
extends SeqLike[A, Repr]


trait SeqLike[+A, +Repr] 
extends IterableLike[A, Repr]

trait IterableLike[+A, +Repr] 
extends Equals with TraversableLike[A, Repr]

trait TraversableLike[+A, +Repr] 
extends HasNewBuilder[A, Repr] with AnyRef

def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
// 因为(x => (x.length, x)) 是 (String) => (Int, String) 
// 所以B:(Int, String)
// That则根据显式声明的结果推断:也就是That = Map[Int, String]

Scala 3

改进的implicit

在Scala 2中,implicit 有三个用途:

  1. 定义隐式值
  2. 方法接收隐式参数
  3. 定义隐式类实现方法扩展

Scala 3有针对性地将其拆解成三个关键字:given usingextension
given用来定义隐式值(如果要导入包或类的given,必须显式标明),using用来在上下文中寻找一个given来赋值给形参,extension用来进行类扩展

上下文函数

scala3 引入了?=>表示上下文函数,写法如下所示:

given Int = 3000
val g: Int ?=> String =
  ($int : Int) ?=> s"Got: ${$int}"

上文的 Int ?=> String 符号表示函数 g 返回 String 类型,而左侧的 ($int : Int) 是一个 using 闭包,编译器将优先从上下文环境中选择一个合适的 Int 值赋给变量 $Int。

传名的上下文参数

using clause 允许接收传名调用,如下方代码块的 cxInt。此时若传入的 x 为 null ,那么 complexInt 实际上就不会被计算。

given complexInt : Int = {
  println("init..")
  1000
}

// using clause 接收传名调用
def CodeC(x : Int | Null)(using cxInt : =>Int): Int ={
  x match {
    case xx : Int => xx * cxInt
    case _ => 0
  }
}

// 打印 init..., complexInt 会被初始化。
CodeC(300)
// 不打印 init..., complexInt 不会被初始化。
CodeC(null)

使用extension扩展Scala类

Scala 2 中通过 implicit class 以实现在不违背 OCP 原则的前提下对类进行拓展,而 Scala 3 使用 extension 替代之,这使得类拓展语义变得更加明确了。同隐式类一样,extension 关键字后必须要指出拓展的类型。另一点需要注意:extension 不能定义在方法内部。

  @main
  def test(): Unit =
    // '<>' 在 SQL 语句中和 `!=` 等价。
    println(1 <> 4)
  end test

  extension (x : Int) 
    infix def <>(that : Int) : Boolean = x != that

交类型和并类型(合取和析取)

看起来和with用法一样

class Person
trait Jumper {def jump() : Unit = ()}
trait Runner {def run() : Unit = ()}

val person1 = new Person with Jumper with Runner
val person2 = new Person with Jumper
val person3 = new Person with Runner

// p 是两个特质的交类型。
def check(p : Jumper & Runner) : Unit = ()

capabilityCheck(person1)	// ok
capabilityCheck(person2)	// error
capabilityCheck(person3)	// error

// 该变量接受 Account 或者是 Email 类型进行身份验证。
var idntfy : Account | Email = Account("ljh2077")

// 该函数的 inf 表明允许接受 Account 或者是 Email 类型。
def verify(inf : Account | Email): Unit ={
    inf match {
        case Account(username) => println(username)
        case Email(address) => println(address)
    }
}

Dependent Function Types

Scala 2 中允许通过 def 定义依赖方法 Dependent Methods。例如:

trait Key { type Value }

trait DB {
  def get(k: Key): Option[k.Value] // a dependent method
}
object Name extends Key { type Value = String }
object Age extends Key { type Value = Int }
val db: DB = ...
val res1: Option[String] = db.get(Name)
val res2: Option[Int] = db.get(Age)

这被称为dependent method type
但是DB的创建非常麻烦:

// a user of a DB
def user(db: DB): Unit =
  db.get(Name) ... db.get(Age)

// creating an instance of the DB and passing it to `user` 必须要匿名内部类
user(new DB {
  def get(k: Key): Option[k.Value] = ... // implementation of DB
})

如果能用lambda替代匿名内部类,不是很方便吗?
Scala 3引入了Dependent Function Types

type DB = (k: Key) => Option[k.Value]

Match Type

type ConstituentPartOf[T] = T match
    case String => Char 	// ConstituentPartOf[String] =:= Char
    case BigInt => Int 		// ConstituentPartOf[String] =:= Int
    case List[t] => t 		// ConstituentPartOf[t]		=:= t

val v1 : ConstituentPartOf[List[Int]] = 1 	// ok
val v2 : ConstituentPartOf[String] = 'a' 	// ok
val v3 : ConstituentPartOf[BigInt] = 20 	// ok

λ Type Lambda

对于一种HKT:

class Functor02[M[_]] :
  def fff() : Unit = ()

type g0 = Functor02[List]

Functor[M[_]] 不能接收 Map 作为类型参数。原因是: Map 自身携带了两个类型参数,不满足 M[T] 形式。
对此需要做一步投影:将携带两个类型参数的 Map 映射为仅携带一个参数的中间类型 IntMap[X]。

class Functor02[M[T]] : 
  def fff[T] : Unit = ()

type IntMap[X] = Map[Int,X]
type g1 = Functor02[IntMap]

new g1().fff[Int]

这种类型投影称为Type Lambda
进一步,为了仅使用一行代码完成投影 ,Scala 2 中需要这样使用非常晦涩的表达:

type g1 = Functor02[({type IntMap[X] = Map[Int, X]})#IntMap]

scala3中则可以简化为:

type g1 = Functor02[[X]=>>Map[Int,X]]

// 延迟确定了 X 类型为 String
new g1().fff[String]

泛型参数类型

// Scala 2
// 只能通过 def 定义泛型方法。
def toMapS2[K,V](k: K,v: V) : Map[K,V] = Map[K,V](k -> v)

// Scala 3
// 柯里化版本
val toMapF2g_0: [K, V] => (K,V) => Map[K, V] = 
  [K, V] => (key: K,value: V) => Map(key -> value) // good

// 非柯里化版本
val toMapF2g_1: [K, V] => K => V => Map[K, V] =
  [K, V] => (key: K) => (value: V) => Map(key -> value) // good

可用于Algebraic Data Type的enum

http://zhangyi.xyz/fp-and-domain-model/
函数范式领域模型的核心要素为代数数据类型(Algebraic Data Type, ADT)和纯函数。代数数据类型表达领域概念,纯函数表达领域行为。由于二者皆被定义为不变的、原子的,因此在类型的约束规则下可以对它们进行组合。可组合的特征使得函数范式建立的领域模型可以由简单到复杂,利用组合子来表现复杂的领域逻辑。
代数数据类型借鉴了代数学中的概念,作为一种函数式数据结构,体现了函数范式的数学意义。通常,代数数据类型不包含任何行为。它利用和类型(Sum Type)来展示相同抽象概念的不同组合,使用积类型(Product Type)来展示同一个概念不同属性的组合。
代数数据类型中的和类型与积类型可以表达领域概念,纯函数则用于表达领域行为。
它们都被定义为不变的原子类型,然后再将这些原子的类型与操作组合起来,满足复杂业务逻辑的需要。

// 枚举类可以携带构造参数。
enum Gender(genderID : Int) {
    // 这里的所有 case 都是一个枚举,它们默认就是继承 Gender 的。
    // 因此没有枚举类不需要参数时,extends Gender 可以省略。
    case Male extends Gender(0)
    case Female extends Gender(1)
}
enum Tree[T] {
  case Leaf(v : T)
  case Node(l : Tree[T], r : Tree[T])
}

无括号缩进语法

可以像写py一样写scala了(误

可变参数拼接


val xs: Array[Int] = Array[Int](1,2,3,4,5)

def f(xs : Int*): Unit =  xs.foreach(println(_))

f(xs : _*)
//Scala 3 中允许使用更简洁的表达方式 ( 类似 Groovy 中的 *xs ):
f(xs*)


val xs : Array[Int] = Array[Int](1,2,3,4,5)
xs match {
    case Array(_,_,others @ _*) => others.foreach(println(_))
}
现在也可以使用一个简洁的 * 符号替代:
val xs : Array[Int] = Array[Int](1,2,3,4,5)
xs match {
    case Array(_,_,others*) => others.foreach(println(_))
}

默认的全局apply

在 Scala 3 中,Dotty 编译器会为所有类提供 apply 方法

infix中缀

symbolic operators 不需要显示加上 infix 关键字。
alphanumeric operator 建议加上 infix 关键字。

main注解

Scala 3 引入了新的 @main 注解,它支持用户将任意一个单例对象的方法标记为程序入口。

将方法赋值给函数

Scala 3 中允许直接将 def 定义的方法赋值给一个函数表达式,官方对此称之 η 拓展 ( Eta Expandition ),该名词源自于 Lambda 演算

不透明类型别称 Opaque

改进的 import 写法

改进的顶级声明

Scala 3 现在支持顶级定义变量和函数,这意味着它们是 “可导出” 的。因此, Scala 3 不再推荐用 Scala 2 的包变量定义全局内容。

柯里化函数重载

特质参数

特质现在可以携带参数列表,被 var 修饰的参数会被视作特质的属性。

字面量类型

Scala 3 支持将数值,字符串直接声明为一个类型。比如:

// 声明字面量类型
val NOT_FOUND : "404" = "404"
val OK : "200" = "200"

// "404" | "200" 表示 code 接收 "404" 或者 "200" 字面量。
// 见下文的交类型 intersection type。
def state(code : "404" | "200") : Unit = ()

state(NOT_FOUND)        	 // ok
state(OK)			// ok
state("200")			// ok
state("500")			// error

Java 17

Java17

  • 文本块,增加了一点parser解析json
  • switch expression,parser解析->,yield
  • record
  • Sealed Classes(通过sealed和permit关键字限制继承权限)
  • Pattern matching for instanceof
  • NullPointerExceptions
  • 压缩数字格式支持
  • Day Period Support Added
  • stringStream新增了tolist方法

Java 23

值类型

当 Java 虚拟机在 20 世纪 90 年代初被设计的时候,从内存取回数据的开销与加法等计算操作相当。依赖于当今 CPU 的多级高速缓存以及指令并行性,一次缓存 miss 可能会导致高达相当于上千次计算操作的开销 —— 相对成本大大增加了。因此,JVM 偏好的常用指针的表现形式(涉及数据块之间的很多间接操作)不再是当今硬件上的理想选择。我们目的是给开发人员提供控制权,使得数据布局与现在的硬件性能模型相匹配,从而为开发人员提供简单的方式来实现平坦(缓存友好)和密集(内存占用友好)的数据布局,又不损失抽象能力和类型安全性。

Valhalla 允许一些类 disavow identity。这些类的实例只是它们的状态,因此可以常规地在包含它的对象或数组中展开,并按值在方法间传递。取舍是我们必须放弃一些灵活性 —— 它们必须是不可变的,不能是布局多态的 —— 但作为放弃这些特性的回报,我们可以获得更平坦、更密集的内存布局以及优化的调用协定。在项目进行过程中,这些类的术语发生了变化;它们最初被称为值类型(value type),后来又被称为内联类(inline class),而现在它们被称为原始类(primitive class)。这个命名突出了它们是运行时具有原始类型行为的类。Valhalla 的口号是:Codes like a class, works like an int.尽管对可变性和子类方面进行了限制,但原始类依然可以使用适用于类的大多数机制:方法、构造器、字段、封装、超类型、泛型参数、注解等。每个级别都有原始类的应用。

除了显而易见的 —— 将内置的基本类型转为真实的类之外 —— 许多 API 抽象,例如数字、日期、cursor 以及类似 Optional 的包装器等也可以自然地建模为 identity-free 类。此外,很多数据结构(例如 HashMap)都可以在其实现中使用原始类来提高运行效率。语言编译器也可以将它们用作数值类型、元组或多重返回等功能的编译目标。

特化泛型

尽管大多数开发者对擦除有一定程度的厌恶,但这种方式具有一个强大的优势,这个优势无法由其他方法获得,那就是:逐步迁移的兼容性。这是一种不破坏现有的源代码或二进制类文件的情况下,把非泛型类兼容的转换为泛型类的能力,同时还让用户和子类具有选择立即迁移、稍后迁移还是不迁移的灵活性。向用户提供泛型,但代价是抛弃他们所有的库,在 2004 年 Java 已经有了一个庞大而活跃的安装基础的前提下,这是一个糟糕的交换 —— 而放在今天会更加糟糕。(有关导致 Java 当前泛型设计的因素的更多信息,请参见为擦除辩护

C++ 20

概念(concept)

类模板,函数模板,以及非模板函数(通常是类模板的成员),可以关联到约束(constraint),它指定了对模板实参的一些要求,这些要求可以被用于选择最恰当的函数重载和模板特化,这种要求的具名集合称为concept,在编译时求值,并在自己被用作约束时成为模板接口的一部分

约束

约束是逻辑操作和操作数的序列,它了指定对模板实参的要求。它们可以在 requires 表达式中出现,也可以直接作为概念的主体。
有三种类型的约束:

  1. 合取(conjunction)
  2. 析取(disjunction)
  3. 不可分割约束(atomic constraint)
    requires 子句:
    关键词 requires 用来引入 requires 子句,它指定对各模板实参,或对函数声明的约束。

python

变量的作用域

通常而言,在编程语言中,变量的作用域从代码结构形式来看,有块级、函数、类、模块、包等由小到大的级别。但是在Python中,没有块级作用域,也就是类似if语句块、for语句块、with上下文管理器等等是不存在作用域概念的,他们等同于普通的语句。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值