This macro would work, but the only problem is
(= (type last) :number)doesn't cut it in terms of checking for ... or function calls -- it fires too easily, since the type of every macro argument tends to be "table".(macro +. [...] (let [len (select "#" ...) last (select len ...)] (if (= (type last) :number) (accumulate [sum 0 i v (ipairs [...])] (if (> i 1) `(+ ,sum ,v) v)) `(accumulate [sum# 0 _i# v# (ipairs ,[...])] (+ sum# v#)))))
[Edited because of a brain blast I had while thinking about this issue and re-reading your reply to me]
I think this can be done in a sub-optimal way without extreme difficulty.
Only the last item being fed to + needs to be checked. All earlier ones are assumed to be summable.
If this last item is ... or a function call, use a loop. Otherwise, just rely on the arg count to generate a sum a + b + c + etc. If the programmer really wants to optimize their adding, they can use local variables to explicitly denote the number of arguments to +. Otherwise, we get predictable DWIM lisp behavior that feels elegant.
I can think of a few solutions for now:
+into a macro that sees how many arguments it's being called with, and only generate a loop if it's a variadic call, else generate
x + y + z ...
Make it an error to call variadic built-in operators, add new library functions
fennel.sum, fennel.mul, etc. that are explicitly made in order to operate on multiple values, and suggest to use those in these edge cases.
The thing is, this is only the tip of the iceberg for weird behaviors of fennel when using the built in math operators. I can't remember what other stuff I ran into, but I'll be sure to document it.
The following functions compile in really abnormal ways:(fn a [...] (+ ...)) (fn b [...] (- ...)) (fn c [...] (* ...)) (fn d [...] (/ ...)) (fn e [...] (% ...)) (fn f [...] (// ...))
The result I got from fennel 1.3.0 is:local function a(...) return ... end local function b(...) return ( - ...) end local function c(...) return ... end local function d(...) return (1 / ...) end local function e(...) return ... end local function f(...) return (1 // ...) end return f
At the bare minimum, it would be helpful if this was some kind of error. Ideally, I would have these compile into some simple loops that are capable of handling things in more intuitive ways. For example, for the first one, something like:function a(...) local out = 0 for i = 1,select('#', ...) do out = out + select(i, ...) end return out end
Thanks for making an awesome language, btw.