└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Kala Lang Design 2 | 3 | 本仓库用于讨论 kala 语言(TODO)的设计与功能。 4 | 5 | 目前本仓库主要用途是记录对该语言的草案(卫星),具体啥时候做出来还得看我要摸到啥时候。讨论请前往本仓库的 [Issue 页](https://github.com/Glavo/kala-lang/issues)。 6 | 7 | ## 概述 8 | 9 | Kala 语言目标是在完全兼容 Java 语言的基础上,提供更强大易用的语言功能,并且绝大部分功能无需引入额外的运行时依赖。 10 | 11 | Kala 语言应该能够完全兼容最新版本 Java 的语法,在此基础上扩展,并允许将编译目标设定为更低版本的 JVM 平台(譬如 Java 8),同时为主目标为低版本平台的程序适配高版本特性(譬如 Primitive Class)提供帮助。 12 | 13 | Kala 编译器工具链应该实现 Java 注解处理器 API,兼容用户已有的注解处理器。 14 | 15 | 应该允许纯 Java 类库通过使用特殊注解提供对 Kala 友好的 API。 16 | 17 | ## 特性 18 | 19 | 一些特性的草案(卫星)。待补充,待完善。欢迎通过 [Issue](https://github.com/Glavo/kala-lang/issues) 或者 [Discussions](https://github.com/Glavo/kala-lang/discussions) 讨论。 20 | 21 | 这些特性细节以及可能的实现方式参见[实现](#实现)一节。 22 | 23 | * 取消跨类常量折叠。 24 | 25 | * 扩展交集类型/并集类型:允许交集类型(`A & B`)和并集类型(`A | B`)出现在所有其他类型可以出现的地方(变量声明,函数参数,函数返回值类型,等等)。 26 | 27 | * 将 `void` 视为类型(待定):允许 `void` 作为类型使用。 28 | 29 | * `this` 类型 30 | 31 | ```java 32 | class BaseClass { 33 | Class getClass2() -> getClass(); 34 | 35 | this-type append(Object obj) {...} 36 | /* 37 | Equivalent to: 38 | @ReturnThis 39 | BaseClass append(Object obj){...} 40 | */ 41 | } 42 | 43 | class MyClass extends BaseClass { 44 | void myMethod() {...} 45 | } 46 | 47 | MyClass myClass = new MyClass(); 48 | BaseClass baseClass = myClass; 49 | 50 | myClass.getClass2(); // static type: Class 51 | baseClass.getClass2(); // static type: Class 52 | 53 | myClass.append(...) 54 | .append(...) 55 | .myMethod(); //ok 56 | ``` 57 | 58 | * Bottom 类型 59 | 60 | ```java 61 | bottom-type exit() { 62 | System.exit(0); 63 | } 64 | 65 | /* 66 | Equivalent to: 67 | @NoReturn 68 | void exit() { 69 | System.exit(0); 70 | } 71 | */ 72 | 73 | int fun() { 74 | exit(); 75 | } // ok 76 | ``` 77 | 78 | * 属性语法 79 | 80 | * 将有 Java Bean 风格的 Getter/Setter 方法的 Java 属性视为 Kala 属性。 81 | 82 | ```java 83 | class C { 84 | static String getField1() -> ...; 85 | static void setField1(String value) -> ...; 86 | 87 | String getField2() -> ...; 88 | 89 | boolean isField3() -> ...; 90 | void setField3(boolean value) -> ...; 91 | } 92 | 93 | C.field1; 94 | C.field1 = "str"; 95 | 96 | var c = new C(); 97 | c.field2; 98 | // c.field2 = "str"; // error 99 | 100 | c.field3; 101 | c.field3 = false; 102 | ``` 103 | 104 | * 简化声明属性的语法。 105 | 106 | ```java 107 | class Test { 108 | public property String name = "Glavo"; 109 | 110 | /* 111 | Equivalent to: 112 | public property String name { 113 | private field = "Glavo"; 114 | public get() -> field; 115 | public set(value) -> field = value; 116 | } 117 | 118 | Or: 119 | private String name = "Glavo"; 120 | public String getName() -> name; 121 | public void setName(String value) -> name = value; 122 | */ 123 | 124 | public readonly property String value { 125 | private field; 126 | private boolean isInitialized = false; 127 | 128 | private String initValue() { 129 | // ... 130 | return someString; 131 | } 132 | 133 | public get() { 134 | if(!isInitialized) { 135 | field = initValue(); 136 | isInitialized = true; 137 | } 138 | return field; 139 | } 140 | } 141 | 142 | public boolean valueIsInitialized() -> this::value.isInitialized; 143 | 144 | public property String title { 145 | private StringProperty field; 146 | 147 | public get() -> field.get(); 148 | public set(value) -> field.set(value); 149 | } 150 | 151 | public StringProperty titleProperty() -> this::title.field; 152 | } 153 | ``` 154 | 155 | * 抽象属性 156 | 157 | ```java 158 | interface I { 159 | readonly property String name; 160 | 161 | StringProperty valueProperty(); 162 | property String value { 163 | default get() -> valueProperty().get(); 164 | default set(value) -> valueProperty().set(value); 165 | } 166 | } 167 | 168 | abstract class C { 169 | abstract readonly property String name; 170 | 171 | abstract property String value { 172 | abstract get(); 173 | final set(value) -> throw new UnsupportedOperationException(); 174 | } 175 | } 176 | ``` 177 | 178 | * 单行方法 179 | 180 | ```java 181 | public String toString() -> $"[${this.name}]"; 182 | 183 | public static String[] newStringArray(int size) = String[]::new; 184 | 185 | public static Pair pairOf(String, int) = Pair::new; 186 | ``` 187 | 188 | * 扩展方法 189 | 190 | ```java 191 | public static void printMe(String this) { 192 | System.out.println(this); 193 | } 194 | 195 | "Hello world!".printMe(); // print "Hello world!" 196 | ``` 197 | 198 | * 成员方法自类型限定 199 | 200 | ```java 201 | interface Seq { 202 | default int sum(Seq this) { 203 | int res = 0; 204 | for(var elem : this) { 205 | res += elem; 206 | } 207 | return res; 208 | } 209 | } 210 | 211 | Seq.of(0, 1, 2, 3).sum(); // 6 212 | // Seq.of("str0", "str1", "str2", "str3").sum(); // compile time error 213 | ``` 214 | 215 | * 嵌套方法 216 | 217 | ```java 218 | void outer(String arg) { 219 | void inner() { 220 | System.out.println(arg); 221 | } 222 | 223 | static int helper(int a, int b) { 224 | // System.out.println(arg); // compile time error 225 | return a * 2 + b; 226 | } 227 | 228 | inner(); 229 | Seq.of(1, 2, 3).reduce(::helper); // 11 230 | } 231 | ``` 232 | 233 | * 局部 `import` 234 | 235 | ```java 236 | void f() { 237 | import static kala.Tuple.*; 238 | import static java.lang.System.out; 239 | 240 | var tuple = of("A", "B"); 241 | out.println(tuple); // print "(A, B)" 242 | } 243 | ``` 244 | 245 | * `TargetName` 246 | 247 | ```java 248 | interface Seq { 249 | @TargetName("sumOfInt") 250 | default int sum(Seq this) {...} 251 | 252 | @TargetName("sumOfLong") 253 | default long sum(Seq this) {...} 254 | 255 | @TargetName("sumOfFloat") 256 | default float sum(Seq this) {...} 257 | 258 | @TargetName("sumOfDouble") 259 | default double sum(Seq this) {...} 260 | } 261 | ``` 262 | 263 | * 嵌套类分离声明(?) 264 | 265 | ```java 266 | class Seq { ... } 267 | static class Seq.Iterator { ... } 268 | ``` 269 | 270 | * 宏(待定?) 271 | 272 | ```java 273 | inline void fun(int arg) = macro funImpl; 274 | Expression funImpl(Expression argExpr) {...} 275 | ``` 276 | 277 | * 语法树(待定?) 278 | 279 | ```java 280 | Expression addExpr = '(1 + 2); 281 | Expression printExpr = '{ 282 | System.out.println($addExpr); 283 | }; 284 | ``` 285 | 286 | * 增强 Record 287 | 288 | * 允许继承自其他父类 289 | 290 | * 以此可以实现在低于 Java 14 的平台上使用 Record。 291 | 292 | ```java 293 | public record Pair(A first, B second) extends Object {} 294 | ``` 295 | 296 | * 使大多数语句成为表达式 297 | 298 | * Block 表达式(待定,与初始化器类的语法冲突,二选一) 299 | 300 | ```java 301 | Object[] arr = { 302 | Object[] tmp = new Object[size]; 303 | for (int i = 0; i < size; i++) { 304 | res[i] = it.next(); 305 | } 306 | tmp; 307 | }; 308 | ``` 309 | 310 | * `if` 表达式 311 | 312 | ```java 313 | var str = if(c1) { 314 | fun1(); 315 | "str1"; 316 | } else if(c2) { 317 | fun2(); 318 | "str2"; 319 | } else { 320 | fun3(); 321 | "str3"; 322 | } 323 | ``` 324 | 325 | * `return` 表达式 326 | 327 | Java 中的 `return` 语句被视为类型为 `bottom-type` 的表达式。 328 | 329 | ```java 330 | var obj = a != null ? a : return null; 331 | ``` 332 | 333 | * `throw` 表达式 334 | 335 | Java 中的 `throw` 语句被视为类型为 `bottom-type` 的表达式。 336 | 337 | ```java 338 | var obj = a != null ? a : throw new Exception(); 339 | ``` 340 | 341 | * 字符串插值 342 | 343 | ```java 344 | public String toString() -> $"MyClass{name=$name, value=$value}"; 345 | ``` 346 | 347 | * 弱化受检异常检测 348 | 349 | ```java 350 | void fun() { 351 | try { 352 | var a = 1 + 2; 353 | } catch(IOException ex) { 354 | // ok 355 | } 356 | 357 | throw new IOException(); // ok 358 | } 359 | ``` 360 | 361 | * 运算符重载(待定) 362 | 363 | * 空安全 364 | 365 | * 可空类型(`T?`)/不可空类型(`T!`)/平台类型(`T`) 366 | 367 | * 识别 Java 中的可空性注解(见 [Kotlin 编译器源码](https://github.com/JetBrains/kotlin/blob/master/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.kt))。 368 | 369 | * 空处理增强 370 | 371 | ```java 372 | Seq? seq = ...; 373 | System.out.println(seq?.toString() ?? "seq is null"); 374 | seq ??= Seq.empty(); 375 | ``` 376 | 377 | * 泛型增强 378 | 379 | * 声明处形变 380 | 381 | ```java 382 | interface Seq { 383 | static Seq from(Seq seq) {...} // Equivalent to: static Seq from(Seq seq) {...} 384 | } 385 | ``` 386 | 387 | * 泛型约束 388 | 389 | ```java 390 | T f(T t) where T extends CharSequence & Comparable -> t; 391 | // Equivalent to: > T f(T t) { return t; } 392 | 393 | T f(T t) 394 | where T extends CharSequence & Comparable, 395 | T super StringBuilder, 396 | T : Reified {...} 397 | /* 398 | Equivalent to: 399 | super StringBuilder : Reified> 400 | T f(T t) {...} 401 | */ 402 | ``` 403 | 404 | * 通用泛型 405 | 406 | ```java 407 | Seq list = Seq.of(1, 2, 3); 408 | ``` 409 | 410 | * ???约束(待定?) 411 | 412 | ```java 413 | trait TraitA { 414 | abstract static T create(); 415 | 416 | abstract void foo(); 417 | } 418 | 419 | class C { 420 | static C create() -> new C(); 421 | 422 | void foo() { 423 | System.out.println("this is C"); 424 | } 425 | } 426 | 427 | T createAndDoFoo() { // Equivalent to: T createAndDoFoo() where T : TraitA 428 | var res = T.create(); 429 | res.foo(); 430 | return res; 431 | } 432 | 433 | C c = createAndDoFoo(); // print "this is C" 434 | ``` 435 | 436 | * 泛型具化 437 | 438 | ```java 439 | class TypeMirror { 440 | static Type getType() where T : Reified; 441 | } 442 | 443 | Type> tpe = TypeMirror.getType(); 444 | System.out.println(tpe); // print "kala.collection.Seq" 445 | 446 | T[] newArray(int size) -> new T[size]; 447 | 448 | String[] arr = newArray(10); 449 | ``` 450 | 451 | ## 实现 452 | 453 | ### 兼容性 454 | 455 | Kala 语言应该能将高版本 Java 语言特性编译至低版本平台。对于纯语法层面的特性(模式匹配,`var` 等),这是一件很轻松的事情,但是对于需要运行时协同的功能(`record` 等),需要寻找其他实现方式实现兼容。 456 | 457 | 有一个值得考虑的选择(后文将该选择简称为“多目标翻译”):支持类似 `kalac --release 8 -d out/ --release 16 -d out-java16/ ...` 的方式指定多个输出路径,为不同 target 目标生成不同的类文件,最后装入一个 Multi-Release JAR 文件中。 458 | 459 | 若确定将要支持多目标翻译,可以考虑在此基础上实现多目标宏,以此简化为不同 Java 版本提供不同实现的方式: 460 | 461 | ```java 462 | class Utils { 463 | #if JAVA_VERSION < 9 464 | private static Unsafe unsafe = with { 465 | try { 466 | Field field = Unsafe.class.getDeclaredField("theUnsafe"); 467 | field.setAccessible(true); 468 | (Unsafe) field.get(null); 469 | } catch (Exception e) { 470 | null; 471 | } 472 | }; 473 | 474 | public static void fullFence() { 475 | unsafe.fullFence(); 476 | } 477 | #else 478 | public static void fullFence() { 479 | VarHandle.fullFence(); 480 | } 481 | #endif 482 | 483 | } 484 | ``` 485 | 486 | ### this 类型 487 | 488 | 可用 `this-type` 关键字(暂定名称)引用的特殊类型。该类型表示该值被使用处的静态类型。 489 | 490 | `this-type` 与协变的类型参数遵循类似的规则,不能作为方法参数类型使用。 491 | 492 | 可以通过 `ReturnThis` 注解(暂定名称)一个方法,该方法返回值类型应为所处上下文中 `this` 的静态类型的超类型(待定:或者 `void`),对于这样的方法,kala 将其返回值类型视为 `this-type`。 493 | 494 | 翻译方案:`this-type` 应被擦除为上下文中 `this` 的静态类型。`this-type` 作为方法返回值类型时,为方法添加 `ReturnThis` 注解。其他位置使用 `this-type` 的具体翻译方案待定。 495 | 496 | ### Bottom 类型 497 | 498 | Bottom 类型是所有类型的子类型,可用 `bottom-type`(暂定名称)引用该类型。 499 | 500 | 被 `NoReturn` 所注解的方法返回值类型被视为 `bottom-type`。 501 | 502 | 作为方法返回值类型时,该方法会被翻译为被 `NoReturn` 注解的方法,其返回值类型为 `void`。当该方法覆盖或实现其他方法时,会合成与超类或超接口中函数签名相同的桥方法。 503 | 504 | (fixme:该翻译方案下,向超类/超接口添加方法或变更方法签名可能导致链接错误或无法正常覆盖,需要重新编译。) 505 | 506 | 其他用途下翻译方案未定。 507 | 508 | 调用返回值类型为 `bottom-type` 的方法后的代码被视为不可达代码。 509 | 510 | ### 属性 511 | 512 | kala 支持属性语法。 513 | 514 | kala 属性的基础语法: 515 | 516 | ```java 517 | 属性修饰符* 'property' 属性类型 属性名称 属性体? ';' 518 | ``` 519 | 520 | 可用于属性的修饰符有: 521 | 522 | * `public`、`protected` 和 `private`:修饰属性的可访问性,默认为包内可见。 523 | * `readonly`:软关键字,声明属性是只读的,没有 Setter 方法。 524 | * `static`:声明属性是静态的。 525 | * `abstract`:声明属性是抽象的。 526 | 527 | 属性可以使用 `值或类名::属性名` 引用,在其后使用 `.` 号访问属性的成员。 528 | 529 | `值或类名.属性名` 等价于 `值或类名::属性名.get()`,`值或类名.属性名 = 值` 等价于 `值或类名::属性名.set(值)`。 530 | 531 | 属性体可以省略。省略属性体时,kala 会生成默认的属性体: 532 | 533 | ```java 534 | 如果属性是抽象的: 535 | { 536 | 与属性相同的可访问性修饰符 abstract get(); 537 | 与属性相同的可访问性修饰符 abstract set(value); 538 | } 539 | 540 | 否则: 541 | { 542 | 与属性相同的可访问性修饰符 与属性相同的静态修饰符 field; 543 | 与属性相同的可访问性修饰符 与属性相同的静态修饰符 get() -> field; 544 | 与属性相同的可访问性修饰符 与属性相同的静态修饰符 set(value) -> field = value; 545 | } 546 | ``` 547 | 548 | 属性体内可以使用声明这些属性成员: 549 | 550 | * 字段声明:默认被翻译为类中名称为 `属性名$字段名` 的成员。字段名不可为 `field`。 551 | * 方法声明:默认被翻译为类中名称为 `属性名$方法名` 的成员。 552 | * `field` 声明:使用软关键字 `field` 声明启用属性的后备字段,默认字段名称与属性名相同。`field` 前可以使用访问性修饰符修饰,也可以可选的明确声明其类型(默认类型与属性类型相同,但可以声明为其他类型)。`field` 可以被修饰为 `final` 的。 553 | * Getter 声明:使用软关键字 `get` 声明属性的 Getter。Getter 声明与方法声明类似,但不可以声明返回值类型,其返回值类型永远与属性类型相同。Getter 默认使用 Java Bean 风格的名称。 554 | * Setter 声明:使用软关键字 `set` 声明非 `readonly` 属性的 Setter。Setter 声明与方法声明类似,但不可以声明返回值或参数类型。其参数列表只能声明单独一个名称,用于指代其参数。其参数类型永远与属性类型相同,返回值类型永远为 `void`。Setter 默认使用 Java Bean 风格的名称。 555 | 556 | 只有抽象类或接口可以有抽象属性,只有抽象属性才可以有抽象的属性成员。 557 | 558 | 接口中的抽象属性的成员函数(包括 Getter 和 Setter)可以声明为 `default` 并提供默认实现。 559 | 560 | ### Record 561 | 562 | 增强 `record` ,允许支持指定其他超类。 563 | 564 | 在超类显式或隐式被指定为 `java.lang.Record` 时才会被真正视为 Java 中的 `record` 进行翻译,完全遵循 [JEP 395](https://openjdk.java.net/jeps/395) 中的限制。 565 | 566 | 当使用其他类作为超类时,不为类生成 `Record_attribute`。 567 | 568 | (多目标翻译:在未显式指定超类时,为高于 Java 16 的目标生成超类为 `java.lang.Record` 版本,为低于 Java 16 的目标生成超类为 `java.lang.Object` 的版本) 569 | 570 | ### Sealed Class 571 | 572 | (待确认)应该可以为任意版本目标生成 `PermittedSubclasses` 属性,低版本平台会自动忽略该项,但不确认 Java 15/16 是否必须在运行时开启 `--enable-preview` 选项。 573 | 574 | ### Primitive Class 575 | 576 | 必须保证实现多目标翻译才能正确应对 Primitive Class。 577 | 578 | 对于 Primitive Class 在低版本平台上的翻译,应该为其生成工厂方法(`` 方法),将对该类型的 `new` 调用翻译为工厂方法调用而非构造器调用。 579 | 580 | TODO:对 `Q` 类型的兼容方案。 581 | 582 | ### 通用泛型 583 | 584 | 使用 Universal Generics 的 [JEP 草案](https://openjdk.java.net/jeps/8261529)中所描述的方式翻译。 585 | 586 | ### 空安全 587 | 588 | 对于引用类型,`T?` 和 `T!` 与 `T` 的擦除一致,由编译器进行流敏感分析验证可空性。 589 | 590 | 对于原始类型,`T?` 应该被擦除为其包装器类型。 591 | 592 | `T` 与 `T? ` ,`T` 与 `T! `,两组类型之间允许非受检的转化。。 593 | 594 | 允许以下不同的参数化类型之间的互相转化: 595 | 596 | * 将参数化类型的类型参数由平台类型修改为对应的可空类型或非空类型,反之亦然。 597 | 598 | ```java 599 | Seq seq = ...; 600 | Seq seq1 = seq; // ok 601 | Seq seq2 = seq; // ok 602 | ``` 603 | 604 | * 将参数化类型的类型通配符边界由平台类型修改为对应的可空类型或非空类型,反之亦然。 605 | 606 | ```java 607 | Seq seq = ...; 608 | Seq seq1 = seq; // ok 609 | Seq seq2 = seq; // ok 610 | ``` 611 | 612 | * 递归地将以上转化规则应用于参数化类型的所有类型参数或通配符边界。 613 | 614 | ```java 615 | Seq> seq = ???; 616 | Seq> seq1 = seq; 617 | Seq> seq2 = seq; 618 | ``` 619 | 620 | 以上规则也适用于方法覆盖。 621 | --------------------------------------------------------------------------------