lantiga / ki

lisp + mori, sweet.js
ki-lang.org
MIT License
478 stars 28 forks source link

arguments var missing in ki functions #25

Open orlin opened 9 years ago

orlin commented 9 years ago

Sometimes it's useful to have the arguments var available, just like js functions offer. Some cases can't quite be covered with variadic arity / multimethod functions. For example variable args with last arg a callback, or whatever other edge-case scenario. In any case, people could write their functions in ki without missing javascript's arguments or falling back to javascript for that reason. Otherwise useful just for the sake of matching-up the host language.

lantiga commented 9 years ago

This one is tricky. ki uses a lot of intermediate immediate function invocations to achieve proper lexical scoping and at the same time the JavaScript arguments object only contains the arguments provided to the enclosing function.

What we could do is store the content of the arguments object in a ki-specific args (or whatever) object which gets overwritten only when a ki fn is invoked. Then we'll have to make sure the previous value of args is put back when the function returns.

I'm wondering whether this is the right thing to do as opposed to trying to cover the edge cases directly. Any thoughts? Also, can you provide an example of edge case with code? Thanks!

lantiga commented 9 years ago

Actually, it's easier than I thought.

Let's start from the current state of things: arguments is correct and available right after entering the function body, e.g.

ki require core
ki (do
     (defn f [] (prn arguments))
     (f 1 2 3))

prints { '0': 1, '1': 2, '2': 3 }, since there's no intermediate immediate functions in the way at that point. However, the only way to have arguments survive the rest of the scope is to assign it to a variable. However, using let wouldn't work, because the assignment happens within an immediate function (for lexical scoping reasons), which is passed no arguments. In fact,

ki require core
ki (do
     (defn f [] (let [args arguments] (prn arguments)))
     (f 1 2 3))

prints {}.

However, we can get to it with the help of a macro

ki require core
ki macro (defargs $args) (js var $args = arguments)
ki (do
     (defn f [] (defargs args) (let [foo 1 bar 2] (prn args)))
     (f 1 2 3))

prints { '0': 1, '1': 2, '2': 3 }.

Feel free to let me know your thoughts.

noncom commented 8 years ago

Much better than nothing! This is a working solution. And considering that the args variable is not used often, making this obvious call is even better, which will also remove the unnecessary overhead where args is never used.. So maybe putting (defargs ..) and some argumentless form of it like (use-args) into the core would be good.

lantiga commented 8 years ago

Thanks for the feedback.

Another couple of (not necessarily better) options

(letargs arg_binding ...)

ki require core
ki macro (defargs $args) (js var $args = arguments)
ki (do
     (defn f [] (letargs args (let [foo 1 bar 2] (prn args))))
     (f 1 2 3))

(defnargs [arg1 arg2 ...] ...)

ki require core
ki macro (defargs $args) (js var $args = arguments)
ki (do
     (defnargs f [] args (let [foo 1 bar 2] (prn args)))
     (f 1 2 3))