凹语言中文语法探讨(二):函数调用

说明

上一篇探讨帖子收到不少有价值的建议,所以我继续贴出来新的一篇探讨。

本篇的话题是“函数的调用”

注:函数的定义格式远比调用复杂,所以打算单独开篇讨论。

函数调用

凹语言英文版的函数调用是最普遍的()形式:


print("hello")

方案1. 中文版的原始设计如下:


打印:"hello"

加:1、2

乘:(加:1、2)、(减:3、4)

  • 用做函数调用的提示符

  • 换行表示调用结束

  • 顿号表示参数的分隔

  • ()用来实现多曾嵌套。

这种设计实际上是前缀表达式,所有的动作名都在开头,用:提示。

由于普通情况下不需要括号,所以表达式更接近于自然语言。

多层嵌套情况下,这种语法和Lisp之类语言的S表达式基本一致。

缺点:

  • 与常见的函数调用并不一致,需要学习成本。(常见的函数调用实际上是中缀表达式)

  • +-*/等运算符的中缀顺序不一致。

  • 在原版中不但用于函数调用,还用于代码块的开端,两者容易混淆。

  • 由于只有前缀提示,无法简单地表达无参数的调用,比如print()

方案2. 直接沿用传统的()形式:


打印("hello")

加(1,2)

乘(加(1,2)、减(3,4))

这种方案的优点是和英文版一致,学习成本为零。

缺点是括号的使用位置与中文自然语言不同,读起来有点生硬。给人一种“这和英文编程有什么区别,就只做了个关键字替换吧”的印象。

方案3. 直接用空格:


打印 "hello"

加 1 2

乘 (加 1 2)(减 3 4)

这种方法其实就是最简略的S表达式,只不过把最外层的括号去掉了。

优点是很简洁;缺点是“含空格量”超高,但中文并不是一门空格语言。

方案4. 用符号调用:


打印!"hello"

加!1、2

乘!(加!1、2)、(减!3、4)

准备!;启动!;加速!;保持!;停止!

这种方法相对于:有两个好处:

  • 与代码块的:不混淆。

  • ;配合的话,可以在同一行写下多个调用,且支持空参数。

缺点是!太惊悚,有点辣眼睛。欢迎大家提供其他的前缀提示符号供选择。

方案5. 用其他的包裹性符号直接替换()

如果换成「」,则可以写成:


打印「"hello"」

加「1、2」

乘「加「1、2」、减「3、4」」

准备「」;启动「」;加速「」;保持「」;停止「」

优点是比()更适合中文阅读体验,缺点是输入法不好。

小结:

函数调用基本就两种形式:

  • 传统中序表达式,以包裹性符号分隔函数名、参数起始和参数结尾。

  • 前序表达式,类似与LISP。

上面的说的几种方式都属于这两类:

  1. :类S表达式,无显示结束符。

  2. ():传统中序表达式。

  3. 只用空格:类S表达式,无显示结束符。

  4. :前序表达式,无显示结束符。

  5. 「」:传统中序表达式。是()的直接替换。

2 个赞

个人建议:就直接用英文的语法,编译器前端维护一套语法就行了,不要支持太大,中文编程本就不是给专业吃程序这碗饭的人用的,对于不少专业程序员来讲不管怎么修改都进不了他们的法眼,他们永远会认为这是拖了裤子放屁。

我个人的JavaScript引擎也是同时支持中文和英文前端,主要关注点还是放在库和生态建设上,用户用你的编程语言能做什么才是最重要的,其他的都不重要。

JavaScript的Promise为例:

中文版的Promise(指引):

编辑器原因显得中文标点很丑,但并不影响初学者理解,其实很多脚本语言中文化后代码都很适合初学者理解了。

1 个赞

@zhaopuming 一会试着列一下用例。早先的 一种改进中文 API 可读性的方法:参数不限于在末尾 供参考。

@pengzhen 同意生态建设对于实用性的重要意义。个人认为对语言设计的回顾和审视与生态建设可以并行(在凹语言团队各成员关注不同方面的当下)。对用例的小结 比如此 不仅可以逐渐补上需求分析这步,也可能促进语言设计的改进(之前的变量定义讨论与 去除var关键词 不知是否有关)。

把函数调用的参数列表拆开成几个部分,确实是一个有意思的想法!

实际上,方法的调用方式,就有这么个意思了。

最早C++在做面向对象时,就是用第一个参数当做对象,后面的参数当做方法参数:

class Point {

    double distance_to(const Point& other) {
         // ...
    }
}

Point x = Point{1, 2}
Point y = Point{3, 4}
x.distance_to(y)

实际上x.distance_to(y)被翻译成了distance_to(x, y)

也就是说,所谓方法调用,就是把 fn_name(x, y, z...)这种形式的调用替换成了x.fn_name(y, z,...)的形式,以增加可读性。

同理,“把(…)替换为(…)”,这种类型的函数调用,也可以达到类似方法之于函数的增强效果。

1 个赞

函数调用用例:

  • 【待补充】匿名函数

  • 具名函数,但无参数。使用函数名进行调用。其中入口函数(main())特殊?

  • 具名函数,有确定个数的参数,且每个参数有其命名。调用时需使用函数名,以及每个参数的对应值。

下为如何应对错误输入:

  • 如果使用了不存在的函数名,会报错

  • 如果使用了过多或过少的参数,会报错

  • 如果使用了类型错误的参数,会报错

非功能需求:

  • 易于将参数与值对应正确,且易于阅读检查。比如通过固定参数顺序或者用<参数名与值>的对,后者就不需要与函数定义时的参数顺序一致。

  • 用户提供函数名,可以提示需要的参数

  • 用户提供参数名,可以提示相关的函数

匿名函数

我初步的设计是这样的(还没实现):

设 数组1 = [1, 2, 3, 4]
数组1.转换:【e→e*e】

这里转换方法相当于array.map,即映射。

【e→e*e】就是一个匿名的lambda函数。

具名参数

这个还没设计,大致可以模仿python:

【和】(甲、乙之数)→数:
    归于:甲+乙
。

曰:和:甲=10、乙=5