lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Counter proposal:
    f1 !  expression2 ! expression1
or
   f1 ! (_expression_-list2) ! (_expression_-list1)

creates a anonymous function and you can call it with one or more parameters, and where express2 can be a function or simple value, or a list of expressions.

All parameters in _expression_-list1 are passed to f1, which will return one or more values (or nil). The first value returned is used as the function which will be called with all other values returned by f1.

This comes with an extension, not allowed with normal function calls as all these become valid:
  nil ! (_expression_ or _expression_-list)
  false ! (_expression_ or _expression_-list)
  'string' ! (_expression_ or _expression_-list)
  {table} ! (_expression_ or _expression_-list)
  userdata ! (_expression_ or _expression_-list)
Their effect is to just return the left-hand-side value without performing any call because they are not a function. As well:
  (1,2,3)! (expressions...)
will just return the list 1,2,3 without performing any call, because 1 is not a function.

This can be used to perform composition (use "!" exactly like where you would use the circle math operator "∘" but write it in the reverse order):
  f1!f2!f3 (x)
will be identical to f3(f2(f1(x))). i.e. "a!b!c" is equivalent to the math notation "c∘b∘a"

Why do I add the "extension" above? This allows one of the functions given in the composition to not just return one value but also provide other computed values to the "chained" function (note that this still uses trailing calls, so it is efficient in Lua except that this is not a simple unconditional jump, but performs a type-test on the first "popped" returned value before jumping, and if it is not a function no jump occurs and this is a normal return of one or more values oncluding the first 1 which is not a function but can be nil, or a number, string, table...).

This allows an efficient way to create "filters" that can process an input and pass additional data to the next filter.

Note that "!" here becomes an unary operator, which creates an anonymous function from a left-side list of _expression_ of any types. and writing "false!" is valid, it is the constant anonymous function that returns false. As well "nil!" is valid and is an anonymous function returning nil. The compiler can avoid creating these anonymous functions for "x!" when it knows that "x" is constant or is not a function, or if the anonymous function is used to perform a call immediately with the parameters given on the right side, so:
  "filter!f (x)" will do "(function() local temp,rest = filter() ; if type(temp)=='function' then return temp(rest); end)(x)"
  "filter!f(x)" may be compiled as "filter(f(x))" if the compiler knows that 'f' is a function.
  "false!( f(x) )" will just be compiled as "(function() f(x); return false end)()", i.e. it will compute f(x) then discard its value to return false directly.
  "math.atan ! math.sin" is just the math composed function "sin∘arctan"
  "math.atan ! math.sin ! y/x" is simply "sin(atan(y/x))"

Filters however can be much more useful when they return multiple values. their first parameter is the conventional "standard input", the other parameters are contextual information that an be passed (modified) to other filters written after the "!" unary operator.

Note that this "!" unary operator is always itself left-associative, when "chaining" them, the calls will always be performed from left to right, but the first call will be with the parameters at end of the "a!b!c!...!z" list where "a(z)" will be called first after evaluating "a" and "z" (the next call will be b(...) if a(z) returned a function.