关于类型的位置
Chanyon opened this issue · 1 comments
nature变量/函数参数返回值类型位置不统一:
//nature
type Rectangle = struct {
int length
int width
var area = fn(self s, Foo f):int {
return s.length * f.width
}
}
//参考c
type Rectangle = struct {
int length
int width
int area (self s, Foo f) {
return s.length * f.width
}
//返回元组
(int, string) foo () {
return (1, "")
}
}
//参考zig
type Rectangle = struct {
length:int
width:int
fn area (s: self, f: Foo) int {
return s.length * f.width
}
//返回元组
fn foo () (int, string) {
return (1, "")
}
}
//参考go
type Rectangle = struct {
length int
width int
fn area (s self, f Foo) int {
return s.length * f.width
}
//返回元组
fn foo () (int, string) {
return (1, "")
}
}
//参考rust
type Rectangle = struct {
length:int
width:int
fn area (s: self, f: Foo) -> int {
return s.length * f.width
}
}
在zig/rust中变量,函数参数/返回值类型位置
example:ziglang
const Rectangle = struct {
length: usize,
width: usize,
pub fn area(s: *Rectangle, f: Foo) usize {
return s.length * f.width;
}
};
example: rustlang
struct Rectangle {
length: usize,
width: usize,
}
impl Rectangle {
pub fn area(self: &self, f: Foo) -> usize {
return self.length * f.width;
}
}
非常感谢你的关注,这个 issue 我会暂时保留,这个问题我也会持续的关注与思考。
在最早的一版设计中,所有的类型都是前置的,函数中的类型语法也是我思考最久的一个语法,这是之前的一些想法,并且都是实现过的。
https://github.com/nature-lang/nature/blob/master/examples/x_function_pre.n
https://github.com/nature-lang/nature/blob/master/examples/x_function_void.n
https://github.com/nature-lang/nature/blob/master/examples/x_closure.n
尤其是函数的返回类型前置,可以省略到 fn 关键字,减轻编码负担。如果函数没有返回值就使用 void 引导。这是几个常见的示例
// 全局函数的定义
int bar(bool b, string s) {
}
// foo 接受一个函数作为参数
void foo(int(int, int) sum) {
sum(1, 2)
}
// 调用 foo 函数并传递匿名函数
foo(int(int a, int b) {
return a + b
})
// 将函数赋值给变量
var f = int (int a, int b) {
return a + b
}
// 自定义 type
var f = custom(my_int a, int b) {}
// 后续会支持的单行函数
int square(int a) = a * a
再来看看返回参数类型后置,并通过 fn 关键字引导函数定义
// 全局函数的定义
fn bar(bool b, string s):int {
}
// foo 接受一个函数作为参数
foo(fn(int, int) sum) {
sum(1, 2)
}
// 调用 foo 函数并传递匿名函数
foo(fn(int a, int b):int {
return a + b
})
var f = fn(int a, int b):int {
return a + b
}
// 自定义 type
var f = fn(my_int a, int b):custom {}
// 单行函数
fn square(int a):int = a * a
客观评价就是两种方式都有优势,类型前置在全局函数声明可以省略一个引导关键字,且和 c/c#/java 等语言保持一致。
但是将当函数作为表达式时使用时,尤其是匿名函数与自定义返回类型函数时,会导致代码的不可读,没有办法一眼就看出来这是在定义函数还是在调用函数。
我的看法是人阅读代码的顺序和编译器解析代码的顺序时一致的,以 var f = custom(my_int a, int b) {}
类型前置时编译器解析等号右边的值的解析顺序是
- custom,此时不确定是类型还是函数名称,不确定是函数调用还是函数声明
- ( ,左括号
- my_int,此时不确定是类型还是函数名称,不确定是函数调用还是函数声明
- a, 连续两个 ident, 可以确定此时是函数声明。
可以看到编译器需要解析到第四个符号时才能确定这个表达式是一个什么表达式,那么人在阅读代码时是否会遇到这样的问题呢?如果是简单的将函数作为表达式,那人类应该可以一眼就确定这是函数的定义还是函数的调用。但是当函数表达式处在一个复杂且嵌套的位置时,人类依旧不能马上看出这是什么表达式。会面临和编译器一样的问题。 这就增加了代码的阅读难度。
而如果使用 fn 在表达式中引导函数的定义,则可以快速的区分这是一个函数的定义还是在函数的调用。同样函数的类型声明也会从 int(int, int)
变成 fn(int,int):int
从我的角度来看,我更喜欢后者,虽然多了 fn符号,但是可读性会更好。
到这里就需要思考几个问题
- nature 中所有的类型都是以前置为原则的,我能否在这里打破这个规则,让函数的返回值类型后置?
- 我能不能仅仅当函数作为闭包表达式时才使用 fn 关键字引导,当存在 fn 关键字引导时函数的返回值类型退化成后置,而全局函数的声明依旧保持类型前置?
- 这不是一个问题,我依旧是希望整体类型前置,我不希望有
var a:int = 12
或者a int := 12
类似这样的变量声明方式,可以是 int a = 12 或者是 var a = 12, 无论是从输入的数量还是阅读体验上都会更好。 - 在类型前置指针中的 * 一定需要放在类型的后面,类型后置时指针的 * 一定要放在类型的前面吗?
- nature 会拥抱函数式编程吗?
关于第一个问题,我的答案是可以,我是用这个两个理由说服自己的,
- 有一个世界上最好的语言已经这么做了
- 函数的输入和输出本质上就是进入和出来,从前面进去,从尾巴出来也算是合情合理吗 🤣
关于第二个问题,现阶段我觉得应该保持一致性,但是这不是绝对的。如果存在合适的时机应该开放出来两种情况
关于第四个问题,以 C 为例子,C 语言中指针通常有两种写法, 第一种是。int *foo;
这里的 * 可以理解为 value_of(foo), 也就是对 foo 进行取值后会得到一个 int 类型, int* foo
则可以直接理解为 foo 是一个指针类型数据,指针指向的数据区块是一个 int 类型的数据。而 golang 中的 var foo *int
则可以明确是指针的类型,而不是类型的指针。我在网上听到一种言论是关于类型前置时指针类型非常的不可读,类型后置时能够更加容易的区分指针。
但是从我的角度来看,类型前置时 * 并不是必须位于类型的后面,同样也可以位于类型的前面,且类型前置时将 * 放在类型的前面可以更加的确定类型。
不过对于复杂指针我还没有思考到位,不确定将 * 放在类型的前面是否会有其他问题,所以我还没有开放指针语法,builtin 中必须存在的指针语法,我暂时使用通用的类型标识 ptr<T>
标识指针类型。说这个只是想表达并不是所有的事情是绝对的。
关于第五个问题,其实将函数作为参数或者返回值在函数中编程中时非常常见的操作,我赞同 rust 或者 golang 中做法, nature 也将积极的拥抱函数式编程。