wenyan-lang / wenyan

文言文編程語言 A programming language for the ancient Chinese.
https://wy-lang.org/
MIT License
19.6k stars 1.1k forks source link

泛型、多方法函数、物、类定义 #595

Closed GLanguage closed 4 years ago

GLanguage commented 4 years ago

最近一直在写一些package,写的时候有了些想法。

泛型

在C++中,我们可以用模版完成泛型:

template <class T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

在目前的wenyan中,我们可以使用如下的方式进行近似:

吾有一術。名之曰「取大」。欲行是術。必先得二元。曰「甲」。曰「乙」。乃行是術曰。
    若「甲」大於「乙」者。乃得「甲」。
    若非。乃得「乙」。也。
是謂「取大」之術也。

但是这无法保证「甲」「乙」类型相同,在更复杂的情形中,也无法保证返回值和「甲」「乙」的类型都相同。 不过,鉴于wenyan是编译为其他语言再运行的,不是直接运行的,所以这样严格的类型检查可能不是非常必须。 若想在wenyan中实现泛型,要么需要支持将类型作为参数传递,如javascript中的List(Number)(这其实还涉及到类定义,现在的wenyan还不支持);要么需要支持C++这样的模版语法。

多方法函数

在javascript中,如果将一个函数定义两次,那么只有后面的一个定义会生效。但是,在julia中,“函数”和“方法”是两个概念。同一个函数可以有多个方法(可以理解成“执行函数的方法”),分别适用于不同的参数类型。比如我们可以先定义add(int, int),再定义add(array, array),然后add函数就可以分别适用于两个整型参数和两个数组参数的情况了。在文言中,也许我们可以添加如下机制:

吾有一術。名之曰「相加」。欲行是術。必先得二數。曰「甲」。曰「乙」。乃行是術曰。
    加「甲」以「乙」。乃得矣。
是謂「相加」之術也。
吾有一術。名之曰「相加」。欲行是術。必先得二列。曰「甲」。曰「乙」。乃行是術曰。
    銜「甲」以「乙」。乃得矣。
是謂「相加」之術也。

这样就定义了函数「相加」的两个方法,分别适用于两个整型参数和两个数组参数的情况。但是,由于目前wenyan编译的时候遇到吾有一術就直接翻译成函数,所以最终的结果和javascript一样,只有后面的定义会生效。

目前wenyan的object语法有一些弊端。在物的声明和赋值中,

物之「「甲」」者。數曰三。

得到javascript:

"甲": 3,

可如果我想实现:

"甲": 乙[2],

要是写

物之「「甲」」者,元曰「乙」之三。

就会得到

"甲": 乙,

要是想要写出

"甲": function(a, b) {
    return a + b;
},

那么

物之「「甲」」者。術曰......嗚呼!

不能实现。必须在物的外部先写好函数,再在物内引用,比较复杂。 如果对象中包含方法,那么方法将无法操作对象的其他属性。因为对象的方法也是该对象的一个部分,要想操作该对象的另一部分,就需要用this指代该对象本身,可目前wenyan中并不支持this。 可以说,目前wenyan的物还并不完善,对属性值的非字面量形式支持不够充分(以函数返回值作为属性值、以列或物中的值作为属性值都不支持,只支持通过简单的标识符或者字面量赋值),对对象方法的支持尤其不充分,既无法在对象内部写方法,也无法通过方法操作对象中的其他属性。javascript中的class就是基于object实现的,如果不能将上述的语法弊端很好地解决的话,就无法写出wenyan自己的class literal(基于物的类),只能套用ECMAScript 6中的class语句了。

总结

wenyan作为少有的用中文编程的语言,作为唯一一个用文言文编程的语言,语言特性是极其重要的。但是,在注意语言特性的时候,也要注意语法的合理性和便捷性,以免语法出现漏洞。虽说鱼和熊掌不可兼得,但是我们可以今天烤鱼,明天炖熊掌啊!

ghost commented 4 years ago

我觉得实现泛型的时候可以调用typeOf()函数,再用严格等于===验算,不同就报TypeError。

ghost commented 4 years ago

至于类的定义,可以用「脚本秘术」先用js编,再用宏实现

GLanguage commented 4 years ago

所以我们是否可以考虑一下在wyg里publish一个package来实现类?从而绕过更新编译器?

ghost commented 4 years ago

完全可以@GLanguage,不过我对js不熟悉,可能做不了这个工作😄

ghost commented 4 years ago

毕竟不是每个人都需要这些功能,直接更新编译器可能会让速度更慢

GLanguage commented 4 years ago

OK and I will try my best :-P

ghost commented 4 years ago

Check my repository typeof and See if it can help. You can also check if my code has bugs.

GLanguage commented 4 years ago

The best way to test a package if it has any bugs is to publish it onto wyg and put it into practice, as I cannot find any bugs while reading the code ^_^ And I have a suggestion that, because punctuation is optional in wenyan, so it is better to remove the in the macros, then using 豈「甲」「乙」之類同乎 and 豈「甲」「乙」之類同乎。 both are ok.

GLanguage commented 4 years ago

Moreover, your idea about 泛型 (my poor English...) is great! We can do something like template with typeof and a types list.

GLanguage commented 4 years ago

Classes (names and contents) can be stored in a class-dictionary (or object), and we use a generator function to generate objects of different classes, but there is still a lot of work to do. (继承、构造函数、析构函数、this、调用方法、私有/公有属性、包括...使用泛型的class...)

ghost commented 4 years ago

Don’t worry. My English is poor too 😄I will fix that. I have already created a pr to wyg about my package.

ghost commented 4 years ago

@GLanguage I realized that in ECMAScript Class there is no private properties, only public ones.

GLanguage commented 4 years ago

It can't be better! Have got a lot of work :-P

ghost commented 4 years ago

I think what most problem is, the construction() function in ECMAScript can include any number of arguments, so we can’t only do that with macros. That makes the work even harder. I am also thinking how to do that in a useful, fast and also sophisticated way.

ghost commented 4 years ago

Awwwwwwwwwwwwwful.

GLanguage commented 4 years ago

Hard enough to raise another issue and work for another year.

ghost commented 4 years ago

Agree with that...

ghost commented 4 years ago

It can’t be better if anyone can help... I can’t because I just have a short look to the classes basics today, and I don’t even know what it is before.

ghost commented 4 years ago

A better way is to forget the problem about classes! And start working on objects. 😂 I am already crashed down for that

GLanguage commented 4 years ago

吾生也有涯,而知也无涯。孤岂欲卿治经为博士邪?但当涉猎,见物、类耳。

ghost commented 4 years ago

物和类就够别人涉猎的了😕🙁☹️🥺😩😫😱

ghost commented 4 years ago

我有一种想向开发者寻求帮助的冲动

GLanguage commented 4 years ago

所以现在看来泛型不是一个很important的东西,可以直接由简单的类型判断实现(后来我想了想,好像js这种动态类型语言本来就不强调函数参数和返回值的类型);class才是真正vital的东西,但是class吧......一言难尽。所以,我先试试能不能写出一个method机制吧。毕竟函数方法也挺难的。看来,想要写wenyan正则表达式编译器(没有class你还想构建NFA?还想转化DFA?劝退劝退),用wenyan研究机器学习(没有class你还想制造神经元?还想卷积?还想池化?还想构建隐藏层?单神经元感知机你都造不出来!劝退劝退)什么的,任重而道远矣!

蒹葭苍苍 白露为霜 所谓class 在水一方 溯洄从之 道阻且长 溯游从之 宛在水中央

感盤古開闢,三皇治世,五帝定倫,世界之間,萬物皆有其類。雞者,禽也。狐者,獸也。松者,木也。《文言陰符》者,書也。或曰,禽、獸、木、書,皆既有之類,今欲「造」一類,可乎?又曰,西人可而吾輩蓋不可矣。然有善者,苦修西人造類之術,而弗有果。嗚呼!何時眼前突兀見此術,吾廬獨破受凍死亦足!(有点过了)

GLanguage commented 4 years ago

wenyan代码写多了感觉陈独秀、胡适等人的工作(新文化运动)都白做了:P

LingDong- commented 4 years ago

Thanks for contributing all these ideas! I've been following the discussion, here are some thoughts:

多方法函数

I think this can be realized in the transpiling step. Each function name is serialized into original name+argument types. e.g. add__num_num and add__arr_arr. And either through static type inference or through runtime typeof we pick which function to use right before calling the function each time.

Just saying it is implementable. Not sure if it adds confusion or cause ambiguity in situations. In C++ overloaded function usage for example, one often find oneself putting a bunch of typecasts in front of each argument to make sure it uses the right overload.

This feature can be pretty neat in situations though, so let's think more about this.

Classes

Personally I'm not huge fan of the full class system (private/public, inheritance, multiple inheritance, etc.). I think it is a giant syntactic sugar that went way out of hand. But I do realize it is useful and popular. So best of luck working on that, I'm more than happy to help if you encounter any problems.

Implementation-wise, unless I'm missing something, I think they can be fully simulated with a combination of functions and structs that wenyan currently have already, on top of which macros can be added for cosmetics.

Thanks again the proposals! Happy to see further discussion/progress.

GLanguage commented 4 years ago

妙哉!

  1. 之前确实没有想过用typeof来进行参数类型判断。
  2. class可能需要在wenyan里实现一个子编译器,建立一种类似于C/C++的symbol表的“class表”机制,储存所有class的info,然后通过构造函数来创建对应的实例。总之是个big work。
  3. 执行的时候的确可以通过function和struct模拟class,但是这终究不太方便(而且不支持继承);而且我目应该是只能用来模拟class,但是我上面提到了,的赋值和方法有些让人头疼。如果说不支持匿名函数是为了符合文言文法我能理解,但是不支持元曰「甲」之「乙」真的很费劲,而且不支持this更是使class几乎无法实现:-(
GLanguage commented 4 years ago

还有一点建议, 在C/C++中,头文件仅包含函数声明,函数实现在另一个源文件中,编译时和main.cpp进行链接;头文件的引用机制是将头文件中的全部内容直接#include xxx语句处展开(和wenyan极其类似);但这样容易出现头文件重复引用的问题,导致同一函数的多次相同声明,这在C/C++中会引发error;虽然在wenyan中不会引发error,但是重复的库文件展开会使编译结果出现不必要的冗余(实测,如果先观A书,又观B书,而B书代码里又观了A书,那么A书就会被观两次,在编译结果中会出现两段相同的代码)。C/C++的解决办法是通过选择编译来避免重复引用:

# ifndef XXX_H
# define XXX_H
/* The Header code */
# endif

这样,如果重复引用了,那么除了第一次引用以外,其他所有引用所引用的内容都是空。如果wenyan也能有类似的机制就更好了。

GLanguage commented 4 years ago

Got an idea: We can realize #ifdef and #ifndef by:

吾有一爻。名之曰「有宏」。
姑妄行此。
    宏。
    昔之「有宏」者。今陽是矣。
如事不諧。豈「「虛指」」之禍歟。
    昔之「有宏」者。今陰是矣。
乃作罷。

Now if we define by

或云「「宏」」。
蓋謂「「」」。
注曰「「宏者。空無也。仿西人也。」」

then 「有宏」 will be . Just the same as #ifdef. #ifndef is the opposite.

GLanguage commented 4 years ago

:-( 殆矣。编译器直接忽略了的存在。而且这样只能将代码编译成一个try...catch...语句,并不能真正的实现选择编译,最终就是两段相同的try...catch...语句,反而更冗余了:-(

LingDong- commented 4 years ago

Correct me if I missed anything, but I believe this as well as inheritance can both be simulated with only functions and structs like so:


// how to use `this`
function f(){ // factory for the class
    var that = {};
    that.x = 0;
    that.a = function(){
        that.x += 1; // that is this
    }
    that.b = function(){
        that.a();
        that.x *= 2;
    }
    return that;
}
var y = f(); // class instance is here
console.log(y.x);
y.b();
console.log(y.x)

// how to do inheritance:
function g(f){ // g is factory of subclass, 
               // f is factory of superclass
    var that = f();

    var p_b = that.b;

    that.b = function(){
        p_b(); // call superclass method
        that.x ++;
    }
    return that;
}

var z = g(f); // instance of subclass

z.a()
z.b()
console.log(z.x)

实测,如果先观A书,又观B书,而B书代码里又观了A书,那么A书就会被观两次,在编译结果中会出现两段相同的代码

This has been discussed, we're thinking about solving this problem from the compiler side. The C/C++ style is simple and robust, but not elegant enough :P

GLanguage commented 4 years ago

Woooooooooooooooooooow! Class!


The C/C++ style is simple and robust, but not elegant enough :P

Of course it is not elegant enough to be in wenyan :)

GLanguage commented 4 years ago

Then we can make a function to generate factories, and use factories to generate objects of classes. Great!

ghost commented 4 years ago

If the superclass have a huge quantity of complex functions, then do we have to call them one by one in the subclass? I think a better way to do inheritance is to create a “dictionary” (I’m not sure if it is called that because in Swift we call that a “dictionary”, but I mean sth like [a:b, c:d]) to inherit, and call the functions(b,d in the previous example) by calling “keys” (a,c in the previous example).

LingDong- commented 4 years ago

Sure. I was just giving a simple example. Like you said, dictionary could work:

// how to do inheritance:
function g(f){ // g is factory of subclass, 
               // f is factory of superclass
    var that = f();

    var p = {}; // throw all superclass methods in here
    for (var k in that){
        p[k] = that[k];
    }

    that.b = function(){
        p.b(); // call superclass method
        that.x ++;
    }
    return that;
}

var z = g(f);

z.a()
z.b()
console.log(z.x)

;)

ghost commented 4 years ago

That is exactly what I mean :) thanks

GLanguage commented 4 years ago

What an issue of mixture!

ghost commented 4 years ago

I want to ask for a method to generate {} when compiling into JS, then we can define anonymous functions by 施「function」於「」{...}, that will compile into function(){...}.

GLanguage commented 4 years ago

应该没有这种中英混编方法?推荐脚本秘术。

吾嘗觀「「腳本秘術」」之書。
爪哇國有腳本
var f = function() {
    ...
}
其文如是矣。
吾有一元。曰「f」。名之曰「某術」。
ghost commented 4 years ago

我試了一下施「function」於「){}(」,得到的是const ans_1 = function() {}()

ghost commented 4 years ago

感覺很有希望

ghost commented 4 years ago

线下运行js发现是可以实现的

GLanguage commented 4 years ago

我覺得這十分投機取巧,於代碼可讀性無利。

ghost commented 4 years ago

我完全是为了「物」的问题嘛,不过确实很投机取巧

ghost commented 4 years ago

I found another problem. If I compile 施「function」於「){}(」 it will be function (){}() but 施「function」於){甲}(」 will be function(甲). I think this is related to the definitions of macros.

GLanguage commented 4 years ago

You mean 施「function」於「){甲}(」

ghost commented 4 years ago

Yes, exactly.

ghost commented 4 years ago

This is my prototype

ghost commented 4 years ago

A5E2A119-6EA0-49DB-8019-8E876D40A366

ghost commented 4 years ago

We should find another way to access anonymous functions or closures

GLanguage commented 4 years ago

That's because you use a 「」in another 「」 in a 「「」」 :P