Wenyan: 希望简化函数多个参数的形式

Created on 23 Dec 2019  ·  20Comments  ·  Source: wenyan-lang/wenyan

1. 函数调用

施「某術」於「甲」於「乙」。

目前编译为

某術(甲)(乙)

期望是

某術(甲,乙)

或考虑采用语法区分:

施「某術」於「甲」、「乙」。 //某術(甲,乙)
施「某術」於「甲」再於「乙」。 //某術(甲)(乙)

2. 函数声明

吾有一術。名之曰「無為」。
欲行是術。必先得二數。曰「甲」。曰「乙」。
乃行是術曰
是謂「無為」之術也。

目前编译为函数嵌套的形式:

var 無為 = () => 0;
無為 = function(甲) {
    return function(乙) {};
};

期望是简单的:

function 無為(甲,乙) {
};
syntax

Most helpful comment

Hi there,

You don't need to worry about currying, it is done automatically. You can call the function in the exact same way you do with non-curried functions. It just provide extra functionality to those who want it, and behaves exactly the same for those who don't care.

In fact, most functional programming language enforces currying (Haskell, SML, lisp ...). In JavaScript this is usually harder to do, but wenyan fixes this problem by doing automatic conversion for you!

If you really dislike it, perhaps you can provide an example where non-curried functions are required and perhaps you can convince me :)

All 20 comments

Hi there,

This is a functional programming feature called currying and was deliberately added.

https://en.wikipedia.org/wiki/Currying

See also #322

Thanks!

If language level provide both single and multiple parameters style, it will be much easier to use outer functions without neccessary to currify each of them.

Hi there,

You don't need to worry about currying, it is done automatically. You can call the function in the exact same way you do with non-curried functions. It just provide extra functionality to those who want it, and behaves exactly the same for those who don't care.

In fact, most functional programming language enforces currying (Haskell, SML, lisp ...). In JavaScript this is usually harder to do, but wenyan fixes this problem by doing automatic conversion for you!

If you really dislike it, perhaps you can provide an example where non-curried functions are required and perhaps you can convince me :)

For example

施「Math.max」於二。於三。書之

We get the following js code (cannot run)

var _ans1=Math.max(2)(3);console.log(_ans1);

Math.max is a native api. I have to define new curried versions of many native api functions before using them.

Native API's are not portable across different target languages, and are soon to be replaced by wenyan standard library functions. You can check out development of stdlib (or even better, contribute to it :) in ./lib folder.
Thanks!

I plan to write some online demos (may be a small game) which need to use browser and DOM apis, what should I do?

Hi @cuixiping ,

Don't worry, there are several solutions.

  1. I wrote this little helper that turns any JS function into a curried one:
function curry(f,arity=-1){
    if (arity == -1){ arity = f.length;}
    if (arity <= 1){
        return f;
    }else{
        return function(a){
            return curry(function(...args){
                return f(a,...args);
            },arity-1)
        }
    }
}

For example:

function add_three_things(a,b,c){
    return a+b+c;
}

var f = curry(add_three_things)

console.log(f(3)(2)(5)) //10

and

施「(curry(Math.max))」於二。於三。書之
  1. With arrow functions, simply doing
施「(x=>y=>Math.max(x,y))」於二。於三。書之

isn't that bad.

Hope that helps! And good luck with the demo!

Thank you.

In js, "this" should be considered.
The following code cannot run with your curry wrapper.

var f = curry(document.body.insertBefore);
var node = document.createElement('div');
var ref = document.body.firstChild;
f(node)(ref); //js error

Maybe I have to define a function like:

var 插补元素 = curry(function (a,b,c) {
  return a.insertBefore(b,c);
});

Then use curry

施「插补元素」於「...」。於「...」。

Hi @cuixiping,

Your solution looks good, sorry for the inconvenience. Also, I think it might be a good idea to put all these wrapped functions like this in the same place, so in the main part of the program you can use pure wenyan. And if later there is a stdlib function doing the same thing, you can easily swap it in. How does that sound?

Again, best of luck with your demo, can't wait to see it :)

Can a demo be a folder?
How to run and pass the testing if my demo contains *.wy and also js/html files?

Another question, some api functions can receive variable number of parameters, how to currify them?

xxx.yyy(a);
xxx.yyy(a, b);
xxx.yyy(a, b, c);

The only way I can think of is to make some new functions for each number of parameters?

var 術甲参一 = a => xxx.yyy(a);
var 術甲参二 = curry((a,b) => xxx.yyy(a,b));
var 術甲参三 = curry((a,b,c) => xxx.yyy(a,b,c));

Things will be a little easier if we can pass mutiple arguments.

The second argument to curry is the arity (number of arguments).
So you can do curry(xxx.yyy, 3) curry(xxx.yyy, 2) etc.

The second argument to curry is the arity (number of arguments).
So you can do curry(xxx.yyy, 3) curry(xxx.yyy, 2) etc.

curry(xxx.yyy, 3) curry(xxx.yyy, 2) cannot give correct [this] object.

优秀的编程语言都是不用括号的.
我的建议是,将函数名当做谓词,
使用谓词逻辑来描述,
比如,
print("你好世界"),
你好世界被打印,
或者
打印你好世界,

吾今日作呆魔(demo),愈發深感不能直接多參之苦。如能支持多參,便無需額外.js文件輔助矣。

I find a hack way, use "." and "," can solve my problem. I am waiting the official syntax.

施「某物.某術」於「甲, 乙, 丙, 丁」。

will make

某物.某術(甲, 乙, 丙, 丁)

:smile_cat: :smile_cat: :smile_cat:

Nice trick :) So glad you figured it out!

Or use a new syntax to pass multiple parameters directly.
For example, 平施

| 文言 | JS |
| ---- | ---- |
| 平施「某術」於「甲」於「乙」。 | f(a, b) |
| 平施「某物」之「「某術」」於「甲」於「乙」。 | obj.m(a, b) |
| 平施「某物」之「「甲」」之「「乙」」於「丙」於「丁」。 | obj.prop.m(a, b) |
| 平施「某物」之「「甲」」之「「乙」」之「「丙」」於「丁」於「壬」。 | obj.prop.prop.m(a, b) |
| 夫「某術」。平施其。 | f() |
| 夫「某術」。平施其於「甲」於「乙」。 | f(a, b) |
| 夫「某物」之「「某術」」。平施其於「甲」於「乙」。 | obj.m(a, b) |
| 夫「甲」。夫「乙」。取二以平施「某術」。 | f(a, b) |
```

@cuixiping 用柯裡化可以比較好地解決問題,如我下面代碼這般定義即可:

在綫示例

今有一術。名之曰「以物行術」。欲行是術。必先得一物。曰「体」。一術。曰「用」。
乃行是術曰。
  施「(()=>用.bind(体))」。乃得矣。
是謂「以物行術」之術也。

今有一術。名之曰「柯裡化」。欲行是術。必先得一術。曰「行」。
乃行是術曰。
  施「((x)=>{const args=Array();const len=x.length;function f(y){args.push(y);if(args.length<len){return f;}return x.apply(this,args)}return f;})」於「行」。乃得矣。
是謂「柯裡化」之術也。

今有一術。名之曰「柯裡化不定數」。欲行是術。必先得一術。曰「行」。
乃行是術曰。
  施「((x)=>{const args=Array();function f(y){args.push(y);if(y!=="其物階備矣") return f;return x.apply(this,args.slice(0, -1))}return f;})」於「行」。乃得矣。
是謂「柯裡化不定數」之術也。

今有一術。名之曰「柯裡化定數」。欲行是術。必先得一術。曰「行」。一數。曰「參」。
乃行是術曰。
  施「((x)=>{const args=Array();const len=參;function f(y){args.push(y);if(args.length<len){return f;}return x.apply(this,args)}return f;})」於「行」。乃得矣。
是謂「柯裡化定數」之術也。

或云「「得其矣」」。
蓋謂「「於「「其物階備矣」」」」。

或云「「平施「甲」其參共「乙」」」。
蓋謂「「施「柯裡化定數」於「甲」於「乙」」」。

或云「「平施「甲」其參若干」」。
蓋謂「「施「柯裡化不定數」於「甲」」」。

或云「「平施「甲」」」。
蓋謂「「施「柯裡化」於「甲」」」。

平施「Math.max」其參共三。於一。於五。於三。書之。
平施「Math.min」其參若干。於一。於五。於三。於零。於負一。得其矣。書之。

@cuixiping 用柯裡化可以比較好地解決問題,如我下面代碼這般定義即可:
...

@akira-cn 解決当然是可以解決,但是还不能算比较好地解決,要进行绑定this以及柯里化,既增加了繁复,也稍微降低了运行效率,且最终编译出的代码冗长。略显舍简求繁。

施「某物.某術」於「甲, 乙, 丙, 丁」。 已经能够满足功能要求,缺点是形式hack了,不如直接将
平施「某物」之「「某術」」於「甲」於「乙」。 编译为 某物.某術(甲, 乙) 更好,而不需要用宏转换。

Was this page helpful?
0 / 5 - 0 ratings