几个小的建议 2

审核中的帖子不能编辑真讨厌
有聊天群吗,沟通感觉更方便

  1. 文档中的let mut没改为var
  2. 文档可以增加数据类型的介绍吗,比如二进制怎么表示,有u8,u16这种类型吗,
  3. 如果支持不同的数据类型,最好增加一个num的trait,并且这个num可以加减乘除
  4. 实现3貌似就要支持操作符号重载,建议搞个简单点语法,比如 fn +(){} 更直观
  5. fn关键词感觉其实也没必要,rust和js都有匿名函数,感觉可以学习coffeescript的函数声明,(){}就是函数,需要的时候再赋值给变量

你好,收到建议。
关于用户群,可以添加MoonBit小助手:moonbit_helper,添加后小助手会邀请你进入用户群~

补充下第5点,
let x=()->Int {
}
这里要写let,感觉还是有繁琐,感觉可以学习golang,用:=来做变量声明
x := ()->Int {

}
就简单多了

继续补充第5点, 如果参考golang := 替代let,那么还需要一种语法替代var才比较直观,我建议用 &= ,&的语法含义通常是指向内存地址的指针,表示可变也直观

  • 我们现在用的 let mutvar 才是 deprecated,你是不是说反了
  • u8u16 暂时没有
  • 暂时没有把 Number 这个 trait 加到内建定义里。但用户随时可以自己定义一个:
    trait Number {
      op_add(Self, Self) -> Self
      op_sub(Self, Self) -> Self
      op_mul(Self, Self) -> Self
      op_div(Self, Self) -> Self
      op_neg(Self) -> Self
    }
    
    由于 MoonBit 的 trait 有类似 Go 的 structural 行为,所有内建的数字类型自动就会实现你定义的 Number trait,不需要显式实现。
  • 我们已经有操作符重载了,文档里有一节介绍了。但语法用的 op_add 这种特殊名字。因为这不是一个高频需求,感觉没有专门开特殊语法的必要
  • js 式的 lambda 对 parser 容易带来歧义问题,所以我们暂时选择用 fn。我们有匿名函数,fn 不写名字即可:fn (x) { x + 1 }。最近可能还会加一个更方便的匿名函数语法(还是用 fn 关键字但更简单。细节还在讨论)。let 变量 = 匿名函数 的问题是写不出递归的 local function。在 FP 写法里递归的 local function 是很常见的
1 个赞

let 变量 = 匿名函数` 的问题是写不出递归的 local function,这个其实可以用 @ 指代当前函数(语法来自coffeescript),如果嵌套的,可以先把 @ 赋值给一个常量。
如果不用js的lambda,也可以考虑rust风格的 |x:Int|{},
这样。

没有u8,如何表示二进制的文件,比如一张图片?

我看了下文档,文档的操作符重装没给出具体的参数个数,建议给一个函数签名,写起来比较方便。

二元运算符是两个参数,一元运算符是一个参数,op_get 是自己和 index 两个参数,op_set 是自己、index、新值三个。

文档稍后会更新一下。

  • 如你所说,遇到嵌套的递归 local function 就不优雅了。互递归的 local function 也很难用 @ 来这种特殊自指来处理。加上 local function 定义的 fn f(...) { ... } 语法和 toplevel function 是一样的,所以我觉得这个 local function 语法没有去掉的必要。
  • 虽然没有 u8,但有一个内建的 Bytes 类型,是 u8 数组的语义。每次对 Bytes get 和 set 的时候,虽然类型上是 Int,但实际操作的只是一个 byte

说实话,多层的匿名递归真不是开发程序常见场景。
大家写法可能更多是:
fn x(s){x(s)}
x:=(s){x(s)}
可以不用支持 @
想写递归,就先给函数绑定常量吧。
不过我感觉 x:=x&=这种语法声明函数、常量和变量,比 fn xlet x更优雅,
因为这里暗示函数是一等公民,和其他数据类型一样

明白,文档还没看到Bytes类型,不过感觉是不是会有更多性能开销,我感觉可以有类似JS的Int,但是最好还是提供一套 u8、i8,u16,i16 …的数据类型,方便一些,用不到的人也可以忽略。
比如我开一个扣费系统,必须确保扣费是正数,如果用u64,我就很有安全感。

还有很多时候,我会用到u8的 overflow add

做图片处理的时候,经常会用到这个

除了多层还有互递归。给函数绑定常量再填回去的做法,会把递归变成 deref + indirect call,严重影响性能的。

x := ... 允许递归也有一些问题。因为 x := x + 1 应该当成递归吗?如果前面已经有一个 x 了,那预期的语义应该是用旧的 x 算一个新的 x,而不是递归。但如何判定哪些情况是要递归,哪些情况是不要递归,就会搞得很复杂。

let 的好处是,每一个声明都由一个关键字开启,阅读起来非常醒目。像 x := 这种语法,如果要支持在 x 的位置放模式匹配,那语法又会出歧义问题。然后有了 letx := 就比较鸡肋了。所以以前我们其实有过 x :=,后来删掉了。

这个我们未来应该也会考虑

如果 x 是一个函数
x := x + 1
应该是编译错误吧,函数怎么加+1,函数不支持+1啊

等价的语法就是
let x = (){
let x = x+1;
}
感觉是会编译报错的

而且,位置放模式匹配,这个我不理解什么意思
如果要实现编译器也比较简单,
先拆分词法, 去掉注释,生成一个数组 , [“xxx”,“:=”]
然后遇到 := , 直接把前面的一个变量名认为是 let ,
如果前面不是变量名,那么报错

另外变量声明和rust一样是有作用域的,超出作用域,变量就释放了

比如
let mut x = 1;
{
let x = 2;
}
dbg!(x) // 这里还是1

另外醒目的问题,其实是语法高亮的事情,与这里写法设计无关

x 不是一个函数。x 是一个 Int。因为 x := 的语法不止可以定义函数,还可以定义其他类型的东西。如果要给函数的 x := ... 开递归的洞,那其他类型(比如 Int)要允许递归吗?允许那值的递归语义不对,不允许那函数和其他类型就(悄悄地)产生了行为不一致。

放模式匹配的意思是 let Some(x) = ... 或者 let [ x, y, z, .. ] = some_array 这类东西。这时 = 前面可以有任意长度任意深度的复杂 pattern,歧义就解不掉了。

用let mut可以接受,且对基于LLM AI的代码生成可能还更友好一点。
为了完善类型系统,[^a-z]ASCII符号在MoonBit里使用的频率占比已经够高了,再引入:=&=这些符号组合,则符号产生的视觉干扰已经到了超Go赶Rust的地步(语法高亮也不能从根本上优化这个问题),个人认为有些得不偿失。

1 个赞