auto λ
Here’s a random thought: would it be useful for programming languages to provide a way to mark a function’s arguments such that expressions would be automatically converted (‘boxed’, if that’s not an overloaded term) to lambda expressions (anonymous inline functions) at the point of call?
Some background first: earlier today, Jon Skeet introduced me to the C# null-coalescing operator. For the uninitiated, this allows you to write something like:
string shipToAddress =
order.shipToAddress ?? order.billingAddress ?? order.customer.address;
where shipToAddress
will get set to the first non-null value of the three
expressions listed. The ??
operator is therefore very much like the SQL
COALESCE
function, except that a C# or Java version of COALESCE
— implemented as a regular function — would by necessity evaluate all the
arguments before calling the function, and wouldn’t provide the short-circuit
evaluation that makes the ??
(and &&
, ||
, etc) operators so useful.
Which led me to wonder: would it make sense for a C#/Java-like language to
allow functions to declare that their arguments should be subject to deferred
evaluation? This would not only allow me to write my own short-circuiting
implementation of COALESCE
(obviating the need to create
yet-another-language-token), but also to write other variations as needed:
return the first non-null-non-empty string, for example.
Jon pointed out that my concept of ‘deferred execution’ is exactly equivalent to converting the expression into a lambda expression at the point of call, which is probably an easier way to think about it. So I guess I’m imagining something like the following syntax (which also uses varargs, though the two concepts are orthogonal):
T coalesce(lambda<T> exprs...) {
for (lambda<T> expr : exprs) {
T result = expr.call();
if (result != null) {
return result;
}
}
return null;
}
void foo() {
string shipToAddress = coalesce(order.shipToAddress,
order.billingAddress, order.customer.address);
}
Here lambda<T>
is a type that is functionally (heh) similar to JavaScript’s
Function
type, providing a call()
method that evaluates the expression and
returns the result. The clever part (such that it is) would need to happen at
the callsite — automatically wrapping each expression in an anonymous
function.
One notable disadvantage with this syntax is that it is no longer possible to determine whether an expression is evaluated merely by looking at the callsite — obviously significant for expressions with side-effects.
In this respect, this is similar to the fact that you cannot tell whether the
C++ statements X x; f(x);
will result in the call f(x)
mutating x
—
f()
will receive a copy of x
or a reference to x
, depending on whether
the function is declared as void f(X x)
or void f(X& x)
(and typically C++
style guides will require that the latter is used only with a const
modifier,
precisely to avoid this kind of confusion).
C# avoids C++’s reference-or-value ambiguity by requiring the caller to use the
ref
keyword for pass-by-reference; Java by enforcing pass-by-value for all
primitive types (and by not supporting value types); perhaps a similar approach
for auto-lambda-boxing would sense, though it would be slightly awkward to have
to write the Haskell-style:
string shipToAddress = coalesce(\order.shipToAddress,
\order.billingAddress, \order.customer.address);
… which I suppose brings us full-circle as an argument for expressing behaviour like this via core language operators!