Function shorthand (lambda) notations, #3647
gwhitney
started this conversation in
Design decisions
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
I am pretty sure that most of these things are discussed elsewhere, but searching for them just now proved frustrating, so I thought I would gather the question and possible answers into one place; please if anyone finds relevant discussion else where, create links to/from here.
Currently, mathjs has two ways to define inline functions that one might want to use, for example, as an argument to the map function or the function description in optional chaining
(expr)?.(arg, other):_as the function name, e.g.map(M, _(x) = x^3)to cube every element of M.?.operator, an expression with one free variable is interpreted as a function of that variable.However, both of these conventions have some drawbacks: (1) has the side effect of changing the local context, and can become confusing when there are multiple instances in one expression or block. It's also a bit more verbose than (2), and looks a little odd to use assignment notation to denote a function. On the other hand (2) has the difficulties that it isn't consistent throughout the language, i.e., only applies in the particular contexts that allow it, and that it is potentially subject to variable capture if the surrounding expression is edited.
Therefore, the question is whether mathjs should adopt new or different lambda notations. (Personally, I think it should based on the above observations, and I write about the issue now because I am likely soon to implement one in the nanomath prototype.) If so, what might that look like? Here are some options that have come up and/or are used in other languages:
_, call itλ, so thatλ(x) = x^3is not* an assignment, just ordinary lambda-notation for a function.x => x^3and() => 7and(x, y) =>2x+yfor other arities. This one seems like a clear win to me since this is a javascript-based language anyway. I feel like this in fact should be added, and the only reason to do any other thing in addition is if there is a desire for even more compact notation, which (2) above is.@which has no other current use, or$if we are willing to break existing uses of$at the beginnings of identifiers, or maybe there are other options, like^for this purpose would mostly not conflict with / be easy to disambiguate from exponentiation, and has a feel of "something coming in from above". Functions with more than one argument could be handled by allowing an integer suffix for the special character, as in@1*@2 + @3for the ternary function that multiplies its first two arguments and adds the third. Note that this facility is very analogous to Civet's single-argument function shorthand, for which it uses the&character, which it frees up by using a digraph for the less-commonly-used bitwise and operation. We could also do the same. The Civet documentation also points out there is an issue of what the "span" of these make-an-expression-into a function operators is: doesj = @^3return a function that assigns its argument cubed toj, or does it setjequal to the cubing function? Civet adopts the convention that the expression used to construct the function expands to include everything up to, but not including, the nearest enclosing function call or assignment (or pipe or return or yield, none of which we have, at least at the moment). That seems to work pretty well, but sometimes you do want to (say) take a binary function and make it unary, such as mapping the log to a specific base. You can't saylog(@, 3)because that would be the log base three of the identity function. So Civet has another operator, much like the first one, for "partial function application" that instead expands to just beyond the nearest enclosing function call (but not assignment) and is a syntax error if there is no enclosing function. Civet uses the dot.for this one, but I think that is too fraught especially as it is a top candidate for automatic function broadcasting (see Broadcast operator for mathjs functions? #2440). Personally, I would suggest^^for this -- a not otherwise legal operator that looks like something coming in from above and is a reminder that it expands up farther, So if so,log(^^,3)would be the log-base-3 function. As for the symbol for the expression-function shorthand, I would be fine with either the currently unused@as in the examples I mentioned above, or adopting&as in Civet and moving bitwise and to, say,&&&(since&&is no good as that could only be used for logical and; fine for this to be long since it's not used much). But personally, the only reason I would see to use&is to match Civet; there's no sense of "and-ness" to me for this function creator operator;@feels like a much better fit to me, as it tells you where your function argument is at ;).So to summarize, I plan to implement the javascript-like notation
x => x^3in nanomath, and if I get ambitious, maybe also the@^3notation for the same function, andlog(^^, 3)for log-base-3 function. In particular, I plan to implementf(x,y) = x + yas just syntactic sugar forf = (x, y) => x + yso that we don't need another type of node for function assignment.Beta Was this translation helpful? Give feedback.
All reactions