有一个方法是写测试(持续开发持续测试持续集成…… 你可以利用面向对象多态 moke 模拟对象,或者是像 Aqullian 测试组那本 CEDJ Java 书一样,真实在本地自动部署测试环境执行测试),还有一个更好的方法是不写 bug,当然这个是那些欠打的 Haskell 程序员说的,他们连 syntax error 都不屑给你指出。
当然对我来说,我觉得任何应用程序都是不应该写测试的(因为他们不应该能够让你写错代码,无论是从工具的角度还是文档的角度);绝大部分算法和框架的测试都应该可以用 Haskell
文档懒得读可以尝试和作者从一个角度想问题,而不是被动接受别人的解释,比如:
当你看到 gradle 的
当你看到
当你写复用库的时候,应该从用户的角度看问题,比如:你要定义一个
此时,你发现这个 (getter) 方法是纯函数,它没有副作用,所以你定义
很多其他情况你考虑一些诸如『是不是提供 vararg?』『是不是用
当然对我来说,我觉得任何应用程序都是不应该写测试的(因为他们不应该能够让你写错代码,无论是从工具的角度还是文档的角度);绝大部分算法和框架的测试都应该可以用 Haskell
Test.QuickCheck 的方法来写。文档懒得读可以尝试和作者从一个角度想问题,而不是被动接受别人的解释,比如:
当你看到 gradle 的
from 时,你会想到:我在 task 里用 from 是什么意思呢?task 是将多个文件汇编成一个文件,所以 from 是指定一个输入的意思当你看到
task (type: ...), Task.dependsOn 的时候也会从使用对象工作流程本身的视角看问题当你写复用库的时候,应该从用户的角度看问题,比如:你要定义一个
Collection<*>.isNotEmpty,是要把它作为扩展方法还是扩展属性?此时,你发现这个 (getter) 方法是纯函数,它没有副作用,所以你定义
val Collection<*>.isNotEmpty: Boolean get() = size != 0 很多其他情况你考虑一些诸如『是不是提供 vararg?』『是不是用
Builder?』『这个 out 是什么意思?』的时候一样有效
duangsuse::Echo
有一个方法是写测试(持续开发持续测试持续集成…… 你可以利用面向对象多态 moke 模拟对象,或者是像 Aqullian 测试组那本 CEDJ Java 书一样,真实在本地自动部署测试环境执行测试),还有一个更好的方法是不写 bug,当然这个是那些欠打的 Haskell 程序员说的,他们连 syntax error 都不屑给你指出。 当然对我来说,我觉得任何应用程序都是不应该写测试的(因为他们不应该能够让你写错代码,无论是从工具的角度还是文档的角度);绝大部分算法和框架的测试都应该可以用 Haskell…
Haskell 里一个可用的
*Main> atoi 10 "1234"
100
*Main> atoi 10 "1234"
100020340
*Main> atoi 10 "12345"
10000000200030450
*Main> atoi 10 "12"
120
*Main> atoi 10 "121"
10210
的时候有多么恐慌
由此我们可以得知: 其实测试是必须的…… 如果没有 REPL 我不知道该怎么办
JavaScript 里想写可能更简单
atoi :: [Char] -> Int 是:atoi = snd . atoi'后来我发现我的思想很危险,它不正确,所以我没有进步
atoi' (base, n) (c : cs) = let newbase = base * base in
(newbase, (base * digitToInt c) + n)
atoi' (_, _) [] = (1, 0)
import Data.Char (digitToInt)你们无法想像我看到输出
atoi :: Int -> String -> Int
atoi base = {- snd . -} (flip atoi') 0 {- . reverse -} where
-- atoi' :: (Int, Int) -> String -> Int
-- atoi' (base, n) (c : cs) = let newbase = base * base in
-- atoi' (newbase, (base * digitToInt c) + n) cs
-- atoi' (_, n) [] = n
-- atoi' (_, _) [] = (1, 0) -- 写错了,我忘记了是 L-R 扫描……
atoi' :: String -> Int -> Int
atoi' (c : cs) i = atoi' cs (i*base + digitToInt c)
atoi' [] i = i -- base * 0
*Main> atoi 10 "1234"
100
*Main> atoi 10 "1234"
100020340
*Main> atoi 10 "12345"
10000000200030450
*Main> atoi 10 "12"
120
*Main> atoi 10 "121"
10210
的时候有多么恐慌
由此我们可以得知: 其实测试是必须的…… 如果没有 REPL 我不知道该怎么办
JavaScript 里想写可能更简单
可我这写的是算法,算法的工作成功与否要靠你自己分析…… 比如以上就是我开始想法 naive,没考虑到位置计数法的左右顺序一说。即便理论是对的,实现的时候也要考虑到很多麻烦的因素
而应用程序写的不正确,则往往是你基本功没做好的缘故,也或许是框架文档不好 / 写的太复杂的缘故
我举个例子,Gradle 使用的 PluginResolution Strategy 设计模式,Groovy 里看起来是这样的(我没隐式把
// 用于支持下面 DSL 的代码在 Java 里都是 Boilerplate,还写了一大堆
// 这里,我们要处理一个列表,使得所有插件 artifact 被解析到一个 location,你的程序会加入 Gradle 的这个内部过程,它会给你一个内部的待处理流,让你利用 side effect 来设置你解析到结果
//
}
}
}
这里,首先模型一个和 Gradle 它有差别的就是:Gradle 里你只能用
那加一个模板类,就可以弄出
可是
这样就可以了(实际上也就是把之前该父类型实例的 this 移动到了
知识点:那这个
实际的对应 Java 代码里,类型参数 C 是一个 Type wildcard
因为你看,我们这个
任何使用
这种情况就是泛型型变的『协变』(covariant),在面向对象参数化多态(parametric polymorphism) 的领域里,就是说
比如,
比如,
此外,Kotlin 的类型投影(就是
换句话说,
>>> System::exit is Function1<*, *>
true
不过有时候也不能全用生产-消费者(PECS, producer-extends, consumer-super)模型的
这样就不行了,至于更高级的(比如,
而应用程序写的不正确,则往往是你基本功没做好的缘故,也或许是框架文档不好 / 写的太复杂的缘故
我举个例子,Gradle 使用的 PluginResolution Strategy 设计模式,Groovy 里看起来是这样的(我没隐式把
it 当成 this)// 用于支持下面 DSL 的代码在 Java 里都是 Boilerplate,还写了一大堆
Action<? super T>, Kotlin 里就是简单一个 T.() -> Unit 或者说 typealias Action<T> = T.() -> * 解决。project.pluginManagement {it.resolutionStragegy {// 这里,我们要处理一个列表,使得所有插件 artifact 被解析到一个 location,你的程序会加入 Gradle 的这个内部过程,它会给你一个内部的待处理流,让你利用 side effect 来设置你解析到结果
//
open class Resolve<out T: Any>(protected var resolved: T?)
// 我们使用模型 data class PluginResolve(val requestedId: Artifact.Name, val requestedVersion: Artifact.Version): Resolve<Artifact.Location>() it.forEachPlugin {if (it.requestedId == 'simple-plugin') it.useModule("org.duangsuse:gradle-simple-plugin:${it.requestedVersion}")
// keep it.resolved null}
}
}
这里,首先模型一个和 Gradle 它有差别的就是:Gradle 里你只能用
useModule, useVersion,换句话说是一个变态的 Builder,我们可以加在 PluginResolve 里fun Resolve<Artifact.Location>.initialize() { this.resolved = Artifact.Location() }
val Resolve<*>.initialized get() = this.resolved?.run { initialize() } ?: this.resolved!!
fun Resolve<Artifact.Location>.useModule(m: Artifact.Module) { initialized.module = m } //...
data class Requested(val id: Artifact.Name, val version: Artifact.Version): Resolve<Artifact.Location>() 那加一个模板类,就可以弄出
it.requestedId, it.useModule 的效果了,本身都是 Resolve<*>,然后数据装在子类里面可是
it.requested.id 该怎么实现呢?就是说现在 Resove<*> 得多一个 <C> 类型参数了,open class Resolve<out T: Any, out C>(protected val requested:C, protected var resolved: T?) 这样就可以了(实际上也就是把之前该父类型实例的 this 移动到了
requested: C 里面)知识点:那这个
out C 是什么意思呢? #Java #Kotlinout C 是 Kotlin 的声明处型变,意思就是比如这里有 fun <R> lets(op: Function<C, R>): R = op(this.requested as C) 实际的对应 Java 代码里,类型参数 C 是一个 Type wildcard
void gets(Producer<? extends C> producer) { this.requested = producer.get(); }
这里为什么可以是 Producer<?: C>?其实谈理论谈到什么 upper-bound, lower-bound, subtyping, variance 什么的都扯远了,你只需要知道 Any? 是所有对象的类型、Nothing 不可能是任何对象的类型、Any? 作为 upper-bound 的类型参数,它的取值范围可以是任何类型就对了(这个是好像是全序(total order)?反对称不知道有没有)。因为你看,我们这个
@FunctionalInterface interface Producer<R> { R gets(); }
它这个 R 是所谓的『生产者位置』,就是说作为返回值绝对可以泛型参数类型安全。任何使用
Producer<Any> 的位置,对 Producer<Number>, Producer<String> 一样有效,因为他们只能使用 R gets(),而这个 R 它其实可以是任何 Any 的子类型,String, Int, Long, Boolean, Date 都没问题这种情况就是泛型型变的『协变』(covariant),在面向对象参数化多态(parametric polymorphism) 的领域里,就是说
K<T>, K<T1> 在 T1: T 的时候,存在 K<T1>: K<T> 的关系比如,
<K: Producer<Any>>, Producer<String>: Producer<Any>
这就是 Kotlin 里的 out, Scala 里的 K[+T]
对应的情况就是泛型型变的『逆变』(contravariant),就是说 K<T>, K<T1> 在 T1: T 的时候,存在 K<T>: K<T1> 的关系比如,
<K: Consumer<String>>, Consumer<Any>: Consumer<String>
这就是 Kotlin 里的 in, Scala 里的 K[-T], 因为一个操作连 Any 都能够接受,那 String 也自然没问题喽(不过更高层次的抽象是生产-消费模型而已)。此外,Kotlin 的类型投影(就是
* 那个看起来像是的类型)也做好了类型安全处理Function<*, *> // Function<in Any?, out Nothing>, 这个函数啥都可以接受,啥都可以输出,包括 100% (抛出异常什么的)的类型 Nothing换句话说,
* 在生产者位置是等价 Nothing(底类型, bottom type)、在消费者位置是 Any?(顶类型),反观 Java 静态类型系统是没有这么方便的东西的,何况 Java 连类型推导都没多少>>> System::exit is Function1<*, *>
true
不过有时候也不能全用生产-消费者(PECS, producer-extends, consumer-super)模型的
<R> R lets(Function<? extends C, R> op) { return op((C) this.requested); } 这样就不行了,至于更高级的(比如,
System.arraycopy 的那种不能限制为只返回/消耗 T 的)型变性下次有机会再讲。
duangsuse::Echo
Haskell 里一个可用的 atoi :: [Char] -> Int 是: atoi = snd . atoi' atoi' (base, n) (c : cs) = let newbase = base * base in (newbase, (base * digitToInt c) + n) atoi' (_, _) [] = (1, 0) 后来我发现我的思想很危险,它不正确,所以我没有进步 import Data.Char (digitToInt) atoi :: Int…
module NumRepr (atoi, itoa) where迫真 point-free 简板,没有任何新东西,新瓶老酒。
import Data.Char (digitToInt, intToDigit)
atoi :: Int -> String -> Int
atoi base = foldl (\ac x -> ac*base + digitToInt x) 0
itoa :: Int -> Int -> String
itoa base n = (sign . map intToDigit . reverse . itoa') n where
itoa' i
|i == 0 = []
|otherwise = let (ds, d) = i `divMod` base
in (d : itoa' ds)
sign = if (signum n) == (-1) then ('-' :) else id
第二个 itoa (from) 的支持,我开始居然还打算使用 Maybe :: (* -> *)……
考虑一下我要更新 Share/zhuazhou 和结尾 Dokuss,然后可能去完成 Parser.kt 和 Reflex 解释器
Refelx 这个比较燃,因为我开始设计的比较奇葩,这就是个『表达式语言』,不过所有东西都有可能是变量…… 这门语言的一个特点是,所有 lhs 本身都是(lhs 值),也就是可以 setValue 的东西
这也就导致了,Reflex 很过分地没有任何控制结构,因为你可以用 Infix 实现(可以随便添加 infix 操作符)
fun (xs) [mut accumlator]其中,
for (var i in xs) { accumlator += i }
(var i) 就是一个 Lhs 值…… 我去其实对于动态作用域,非常自然的一种做法是判断名字的分配是否冲突,然后为冲突的情况提供新的层 Map,我称为
VersionizedMap
对于这门语言的值和操作,首先我得为语法上的便利提供条件:2D Syntax 和一些特殊的 case,比如 fun mainloop() loop... 这种值,当然基于 JVM 的对象,就是 Boolean, Number, Char, String, Array, List, Set, Map, Object, Function, Error 要提供内建支持(不过这样的话,函数参数的个数也就有限制了…… 我希望是至多 7 个参数)
此外还要提供 IntegralRange 和 RationalRange
就闭包怎么支持 UpValue 来说,其实就是直接包一个引用就行,毕竟『值』的部分是底层的 JVM 辅助的
Body = (EQ Expr) | (loop)? NL Layout:l Stmt (l Stmt)* —这就不是 CFG 了,不过利用 Parserc.kt,contextual { } 解析也很简单+
fun Name ArgList LocalList Body 是一种语句,它没有值(为了统一就叫 Unit+
loop 也是语句,for 只是一个函数+
when 是表达式,if 不在语言层面特化处理,只是一种内部提供的辅助函数+
do BraceArgList 是表达式以上语法都提供了使用
{} 的版本和 2D Syntax 带语义缩进的版本+ 各种
Name ArgList Block? 是表达式+ 各种
Expr (infix) Expr 是表达式fun (n) loop我打算只给一些基本的循环分支提供默认函数,我们知道,Kotlin 对一些本身不是『硬』关键字的东西,都可能有特殊处理,不过我没有这个觉悟,我觉得它能够好看就可以了。
println(when input()
> n => "Too big"
< n => "Too small"
== n => "OK".also { break })
而且不能到处
break,只能有一层(要不然我的求值器就得使用 Exception 来传递控制流执行权……)欸不对啊,好像只要我提供 closure,就必须使用 exceptions……
像是
if 这种的if (p) {
} else if (p1) {
} else {
} 就是 infix IfBuilder…… 本身就是接受很多 code block 的东西,emmm
既然假设我有精力,就把『困难』一点的多态选择加进来吧,熟悉一下缓存优化什么的…… 也不对,因为 JVM 实现了这个算法我可以拿来用
typedef Operation = Runnable不过这个语法该怎么执行,就成了问题,如果没有延迟求值的话…… 🤔(虽然延迟求值本身不是特别困难,我可以要求求值器认为某种 closure 对象是自动执行的)
infixl else
infix fun IfSyntax.else(if: IfSyntax)
infix fun IfSyntax.else(op: Operation)
(在实现 if 语法的时候,显然即便 if(...){...} 是延迟执行的,第一个 if 的语义也可以被正确实现,对 Builder 的方式也能够支持)
(在提供 block 参数的时候,我的想法是不如就像 Kotlin 一样默认最后一个 SAM 接口吧)
duangsuse::Echo
module NumRepr (atoi, itoa) where import Data.Char (digitToInt, intToDigit) atoi :: Int -> String -> Int atoi base = foldl (\ac x -> ac*base + digitToInt x) 0 itoa :: Int -> Int -> String itoa base n = (sign . map intToDigit . reverse . itoa') n where …
关于 Reflex 这门语言,我这里已经有不少(本来不是为它设计) 的稿子了,但因为都是 JVM 语言所以都可以用,就来汇总一下
看了刚才的示例我感觉非常 excited,分分钟想弄出个大新闻 🐸
这是一门多范式 JVM 编程语言,支持特性包括:
+ 基本的值、计算、顺序、分支、循环、递归
+ 支持高阶函数、Block (closure)
+ Java 没有太多值类型,所以都是按引用传递
+ 支持语言层面的惰性求值(call-by-need),惰性求值是针对函数的返回值、参数,不支持列表等 Iterable 上的惰性求值(要不然不就是支持偏 coroutine 了……)。
如果是保存本地变量,不会对惰性块进行求值,其他情况(包括求值后值不需要的)一律进行求值。
+ 支持语言层面的传递表达式(call-by-name),使用关键字
返回表达式是可行的,返回表达式没有特殊语法,表达式返回时不会被求值,这也就导致了 FFI 上,要
如果使用多态,则参数要加上类型声明;如果不使用多态返回值可以不写,不过一般还是写上(也就是类似 Python 那些四不像,本身就是弱类型的)
+ 允许使用 Java 反射访问任何 Java API,语言层面利用
返回左值的表示对象会针对对象的 getter/setter 访问性选择 Val/Var 接口实现,如果是公开字段,则是 Var,如果只有 setter,则生成的 Var 在 get 时抛出异常
为了方便起见,应该提供
($ 不是有效的标识符组成成分,但是 Reflex 支持
不要被
创建的所有函数,都使用
对象上面的方法,内部使用
均只支持到 7 个参数(方法包含 this)
+ 支持内部对外部接口的重写,比如
+ 包含 Nullability 运算符 (?.) (?:) (!!),它们是内建的,多态也支持可空性判断
+ 可以利用 JVM 的接口进行
+ 标准库扩展了
看了刚才的示例我感觉非常 excited,分分钟想弄出个大新闻 🐸
这是一门多范式 JVM 编程语言,支持特性包括:
+ 基本的值、计算、顺序、分支、循环、递归
+ 支持高阶函数、Block (closure)
+ Java 没有太多值类型,所以都是按引用传递
+ 支持语言层面的惰性求值(call-by-need),惰性求值是针对函数的返回值、参数,不支持列表等 Iterable 上的惰性求值(要不然不就是支持偏 coroutine 了……)。
如果是保存本地变量,不会对惰性块进行求值,其他情况(包括求值后值不需要的)一律进行求值。
+ 支持语言层面的传递表达式(call-by-name),使用关键字
expr 定义一个表达式(语句,按名字引用),表达式作为参数传递不论目标函数的语言都是按值传(要不然,为什么不用 SAM?)返回表达式是可行的,返回表达式没有特殊语法,表达式返回时不会被求值,这也就导致了 FFI 上,要
instanceof 动态检查类型如果使用多态,则参数要加上类型声明;如果不使用多态返回值可以不写,不过一般还是写上(也就是类似 Python 那些四不像,本身就是弱类型的)
+ 允许使用 Java 反射访问任何 Java API,语言层面利用
o.method(_) 来进行 Partial application (WIP,在完成前这种语法是 reserved,并且不可以用 o.method 引用函数)o.property 来获得一个 Var, 它可以使用内部的 (=) 操作符赋值或者直接取值 (o.XXX | o.getXXX, o.setXXX)返回左值的表示对象会针对对象的 getter/setter 访问性选择 Val/Var 接口实现,如果是公开字段,则是 Var,如果只有 setter,则生成的 Var 在 get 时抛出异常
为了方便起见,应该提供
(.$ #) 操作符(# 表示名字参数)来访问对象的非公开属性($ 不是有效的标识符组成成分,但是 Reflex 支持
`name` 来表示标识符),特殊处理 (o.isXXX, o.hasXXX)不要被
(=) 这种表示方法骗了,它不支持类似 Haskell 的 sectioning,不过 Partial application 可以考虑o::method 来引用对象上的一个方法,还应该提供 o ::$ method 以引用非公开方法创建的所有函数,都使用
Runnable, Function0<R>, Function1<T0, R>, Function2<T0, T1, R> 实例来表示,且支持 SAM 自动实现对象上面的方法,内部使用
Method0<A>: Consumer<A>, Method1<A1>, ... 表示均只支持到 7 个参数(方法包含 this)
+ 支持内部对外部接口的重写,比如
val Collection.isEmpty get() = size == 0, fun Any?.copy() = this?.clone()
+ 包含各种可以重载的操作符,+-*/ 一应俱全,只要能够创建内部支持类型值的东西,都应该有特殊的对应语法+ 包含 Nullability 运算符 (?.) (?:) (!!),它们是内建的,多态也支持可空性判断
+ 可以利用 JVM 的接口进行
(is) (as) (as?) 操作,它们是内建接口+ 标准库扩展了
infix fun Any?.in(container: Any?) 来表示 contains 操作https://github.com/lice-lang/lice
冰封哥都退团了,emmm,之前听说实现 expr 是有啥问题来着
冰封哥都退团了,emmm,之前听说实现 expr 是有啥问题来着
GitHub
lice-lang/lice
A multi-paradigm programming language running on JVM - lice-lang/lice
duangsuse::Echo
https://github.com/lice-lang/lice 冰封哥都退团了,emmm,之前听说实现 expr 是有啥问题来着
之前不知道是怎么回事搞混了 call-by-name 和 call-by-need,我以为 by-name 是惰性求值,by-need 是重复求值
肯定不是我自己记岔了!第一次看到 call-by-name 它好像被人拿来做是惰性求值的意思了……
肯定不是我自己记岔了!第一次看到 call-by-name 它好像被人拿来做是惰性求值的意思了……
duangsuse::Echo
关于 Reflex 这门语言,我这里已经有不少(本来不是为它设计) 的稿子了,但因为都是 JVM 语言所以都可以用,就来汇总一下 看了刚才的示例我感觉非常 excited,分分钟想弄出个大新闻 🐸 这是一门多范式 JVM 编程语言,支持特性包括: + 基本的值、计算、顺序、分支、循环、递归 + 支持高阶函数、Block (closure) + Java 没有太多值类型,所以都是按引用传递 + 支持语言层面的惰性求值(call-by-need),惰性求值是针对函数的返回值、参数,不支持列表等 Iterable…
这一段定义了和 Java 的 interop,
+ Reflex 是一门多范式、动态类型的 JVM 语言,它支持多态,在语法上支持类型标记。
+ 控制流,Reflex 支持顺序、分支、循环、递归。
+ Reflex 支持高阶函数和 Block (closure),函数参数都是按
+ 语言层面支持(针对函数参数和返回值的)惰性求值和(针对返回值的)表达式作为值
+ 惰性求值使用标准库
在 Java 的层面看来,收到的对象是
在 Java 的层面看来,收到的对象是
+ 允许使用反射访问任何 Java API,内部语法
如果属性以
+
+
+ 所有带
创建的所有函数,都由
对象上面的方法,内部使用
函数或方法(不包含
+ 可以使用
+ 内部支持
+ 对于 Nullable type 支持
需要注意的一点是,
+ 标准库还扩展了
+ infix operator 定义类似这样:
如果 infix operator 的名字两侧包含空白,代表它需要左/右侧有一定数量的空白字符
如果 infix operator 的最右侧是
+ Reflex 是一门多范式、动态类型的 JVM 语言,它支持多态,在语法上支持类型标记。
+ 控制流,Reflex 支持顺序、分支、循环、递归。
+ Reflex 支持高阶函数和 Block (closure),函数参数都是按
java.lang.Object 引用传递+ 语言层面支持(针对函数参数和返回值的)惰性求值和(针对返回值的)表达式作为值
+ 惰性求值使用标准库
lazy {} 函数创建,如果将惰性块保存到本地变量或者作为 Reflex 函数的参数、从函数返回,不求值。lazy 是软关键字在 Java 的层面看来,收到的对象是
org.duangsuse.reflex.rhs.Lazily
+ 表达式使用 expr Name = Expr : Stmt 语法创建,表达式在每一个要求值的地方求值,表达式可以作为返回值,这种情况返回表达式而不计算,expr 是关键字。在 Java 的层面看来,收到的对象是
org.duangsuse.reflex.rhs.Expression + 允许使用反射访问任何 Java API,内部语法
(.) (::)、标准库 (.$ #) (::$ #) (?.$ #) (?::$ #)
+ o.method 不可以使用,这个语法只能用来表示索引属性 Val / Var
public final o.XXX, public o.XXX 字段、public o.getXXX(), public o.setXXX() 方法如果属性以
is, has 开头,则查找方法时不添加 get/set 前缀+
o.method(_) 进行 Partial application (WIP,目前先 reserve 这个 _)+
o::method 来引用某个 receiver 上的方法+ 所有带
$ 的语法,表示访问非公开属性/字段/方法,都由标准库提供(import std.reflect)创建的所有函数,都由
java.lang.Runnable, org.duangsuse.reflex.fn.Function[0-7] 提供Function0<R>, Function1<T0, R>, ... 对象上面的方法,内部使用
Method0<A>: Consumer<A>, Method1<A1>, ... Method7 实现函数或方法(不包含
this)的参数个数不能大于 7,vararg 关键字可以支持变长参数,内部支持 SAM(Single Abstract Method) 自动代理实现ExprOrBlock = (EQ Expr | Block)+ 可以使用
val Type.Name[: Type] get(L R) ExprOrBlock [ set(L Name R) ExprOrBlock ] 扩展 getter+ 可以使用
fun Type.Name ArgList[: Type] ExprOrBlock 扩展方法+ 内部支持
(is) (as?) (as) 操作符+ 对于 Nullable type 支持
(?:) (!!) 操作符需要注意的一点是,
T? 和 T 的差别在于 T? 可能是 null + 标准库还扩展了
infix fun Any?.in(container: Any?) = container.contains(this)
infix fun Any?.`!in`(container: Any?) = container.contains(this) 方法+ infix operator 定义类似这样:
(infixl | infixr) LitInt (L ... R) infix fun 默认 precedence 和 (.) 相当如果 infix operator 的名字两侧包含空白,代表它需要左/右侧有一定数量的空白字符
如果 infix operator 的最右侧是
#,代表它接受一个标识符作为参数,对应 fun 将被传入 org.duangsuse.reflex.data.Name 实例
duangsuse::Echo
这一段定义了和 Java 的 interop, + Reflex 是一门多范式、动态类型的 JVM 语言,它支持多态,在语法上支持类型标记。 + 控制流,Reflex 支持顺序、分支、循环、递归。 + Reflex 支持高阶函数和 Block (closure),函数参数都是按 java.lang.Object 引用传递 + 语言层面支持(针对函数参数和返回值的)惰性求值和(针对返回值的)表达式作为值 + 惰性求值使用标准库 lazy {} 函数创建,如果将惰性块保存到本地变量或者作为 Reflex…
然后是在操作和内部值的层面:
2D 表示不需要
Literal:
Bool
Int
Real
注意这里
Name (任何在 Java 中有效的标识符,除了包含
typedef Name EQ Type其中,EQ 的判断方法是
const Name EQ Expr
fun Name (L ArgList R)[: Type] [LSQ LocalList RSQ] BlockOrExpr2D : Stmt
do (I ArgList I) | (ArgList.) BlockOrExprNoEQ2D : Expr
loop(L Name R) [(while | until) Expr] Block : Stmt
break(L Name R) : Expr
continue(L Name R) : Expr
WhenBr = when LB { Expr => Expr } [else => Expr] RB
| when NL l:Layout Expr (l Expr)*
WhenInfix = when Expr LB {infix Expr => Expr} [else => Expr] RB | {- 2D part -}
WhenEq = when Expr LB { Expr => Expr } [else => Expr] | {- 2D part -}
When: Expr = WhenBr, WhenInfix, WhenEQ
Var = var Name : Expr
Call = Name (L Args R) [ Block ] : Expr
Infix = Expr infix Expr : Expr {- 大意,因为分 infixl/infixr - }
eval(branch).equals(subject), 方便支持模式匹配2D 表示不需要
where 语法,就可以判定使用基于缩进的块(=) (+) (-) (*) (/) (mod)我们不把负数当数,所以没有类似 Kotlin 的 unaryMinus 语法,但有一个
(>) (>=) (<) (<=) (==) (!=)
neg 一元操作符(还是 std.prelude 里面的)inv 操作没有对应语法Literal:
Bool
true false Int
0xAF 1_23 0b10 0xFFl
其中,下划线不能连续出现Real
0.1 0.1f 1.0e10
Str "abc" "User($name)" "${1+1}" "\t\v\a\b\n\r\f" "\x00" "\u0000" "\U0000_0000" (<<heredoc ...)注意这里
\U 中间的划线是必须写上的Name (任何在 Java 中有效的标识符,除了包含
$ 的部分) `name` (建模的时候我把标识符也考虑进去了,虽然它表示对存储的引用,我觉得必要时它也是值……)上面那两条消息就是 #Reflex 的基本设计了,那 numpreety.js 也不是多好写,我先写点看看 🤔
JavaScript 真是辣鸡,我去找点 ES6 to 5 的翻译器,结果我只找到 Babel 和 traceur 两个,而且 traceur 的那个还一年没更了
Babel 的那个根本不能即装即用,亏得它还弄的花里胡哨的彩色啊…… 还啥啥啥定律,我看以后不如用 Kotlin.js 写好了
ES6 都麻烦的要死,为毛现在还是 Next,明明是必品啊,TMD 我写单文件就麻烦,node 本来已经只是包装了还包装不好,看看人家 Ruby
Babel 的那个根本不能即装即用,亏得它还弄的花里胡哨的彩色啊…… 还啥啥啥定律,我看以后不如用 Kotlin.js 写好了
ES6 都麻烦的要死,为毛现在还是 Next,明明是必品啊,TMD 我写单文件就麻烦,node 本来已经只是包装了还包装不好,看看人家 Ruby
已经足够把 babel 那一些不方便的 ES6 换掉了,虽然不如 Kotlin 好用
Google 不知道是怎么搞的,难道 Google 的前端都是吃干饭的?怎么才弄出那一点东西还不维护了,NPM 上都是几年前更新
Google 不知道是怎么搞的,难道 Google 的前端都是吃干饭的?怎么才弄出那一点东西还不维护了,NPM 上都是几年前更新