-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fn:chain or fn:compose #882
Comments
I'm not familiar with chain...for functional composition its more normal to use an operator. items-where($seq, not#1 >> contains(?,"e")) it also mirrors the use of '=>' |
Indeed, there are a few varieties of arrow that we could assign ( One of the problems in usability terms is the directionality. For functional programming aficionados, writing f(g(x)) as |
tbh, OO languages rarely have function composition, your example If I had to do it in an OO language I would probably write some sort of extension method on
and you'd need x and y in advance, else something like
if you didnt. as xslt and xpath arent really setup in an OO, this would seem dissonant to me. We already have 'pipe' i.e. So I like the suggestion, I'd just be careful of the syntax |
I think the arrow operator has proved so popular for two reasons: firstly, it cuts out the nested parentheses, and secondly, you read it as a pipeline of operations to be performed from left to right, The challenge with function composition is that one ordering seems natural and intuitive to one group of people, and the opposite ordering seems natural and intuitive to a different group. |
I agree, so the arrow operator is popular and a success (even with OOers?) but you think composition as an operator is problematic even though
now we're just discussing whether OOers balk at an operator rather than a method name and a '.'. I think you underestimate OOers (I say so as a former OO zealot) |
Actually,
|
Thanks, good to know. |
its pretty ugly though much better would be (using ".>" as composition as ">>" is taken and the mathematicians "." is quite problematic)
|
I disagree. We have used all possible keyboard symbols for operators and now need more ... Who is to remember all "magic strings" ? Almost no one, even their creators. As for "beauty", beauty is in the eyes of the beholder. Enough with all ugly, long and beyond understanding lambda expressions! What is not so very subjective as "beauty" is understandability. And it has been more than 10 years since we started using "?" in the place of an argument to express partial application. No one has claimed this is "ugly" or not useful. Trying to invent "magic strings" for well-understood concepts that we have had for years is contributing to the pollution of the language. Not to mention that such amount of effort could be directed towards something that could be really useful. |
I don't understand, I LIKE "?" as shorthand for a lambda expression, i wasnt objecting to it. It seems like a very useful and memorable "magic string". what seems less understandable is expression composition in terms of a function that does composition AND application by effectively turning it into a lambda.
` |
For me it is the opposite:
is a function that still needs its first argument, and when given this, it will perform on it the chain of all other specified functions. Absolutely clear. where with:
I need to think hard what And why in one case (to the left) And why should I need to learn and memorize yet even new operators, when we clearly can express this in the current language with consistent use of partial application:
|
Its not obvious to me, and it wasn't obvious to the OP, thus the question, the whole topic is based on it not being obvious. the right to left questions seems odd, the direction is indicated by ">" in exactly the same way as it does for "=>", they are very closely linked, and thus very similarly named. I don't understand the question about "?" being inconsistent to ".>", the analogous expression using "=>" would be
The question is "fn:chain or fn:compose" the main question to me isn't the syntax (operator vs function) rather than the definition. if we choose chain, then we don't need to memorise the function compose. If you set up both alternatives and a compose expression and a chain expression and expressed it in both options, you find that compose is syntactically simpler (in terms of syntax tree size), but not the whole story. As a non XSLT/XPath developer (or at least a relatively ignorant one) I would expect compose to exist, because compose exists in lots of languages, because its a primitive operation on functions, and people are taught in in college on SE/CS courses. I prefer the operator form because a) its syntactically even simpler than "fn:compose()".
I'm sympathetic to the "no more magic strings" message, but that boat seems to have already sailed with "/", "!", "=>", so it would seem operators have to be "magic strings"
|
@MarkNicholls The history of Many people wished the direction to be left to right, and this is how function chain was born. As for whether or not it is more convenient to return a function or produce a result ( People, who are not comfortable with functional programming, will want the result to be directly produced. Otherwise, they must append We don't want to have too-many functions and specifically two functions that are almost the same. This is why I started with multiCompose (from right to left) but taking into account the preferences of the majority, this evolved into fn:chain that is left-to-right and in a most convenient and readable way has the initial (non-function) argument as its left-most one, where it belongs. I think this is a complete explanation why the things are as they are. I will not provide more answers, because all is said in this reply. If you try to convince the majority of people here that right to left evaluation is better and if you succeed, it is simply a mechanical task to turn fn:chain to whatever you are suggesting. As I said, both alternatives are OK with me, but I think people are right to request what they consider the most convenient syntax and evaluation order, and at present fn:chain gives them what they wanted -- without needing a single new operator. |
We get an error: "Expecting '(' " The syntactically valid expression requires using not just a function name, but a function call:
Thus you will need:
Unfortunately, this is not too-elegant, is less readable, and could even be considered rather ugly. |
Also, in JavaScript/Typescript Thus it could be confusing to use |
so the real alternative for multicompose was
To me all both options are not ideal. I think I prefer multicompose, because it captures the primitive notion directly. The order I'm on the fence about, given a function syntax, where right to left application takes precedence, actually right to left seems more consistent, despite the fact I personally find left to right more intuitive. (i.e. (a->b) -> (b ->c) -> (a -> c)) I throw broad generalisations, psuedo code and suggestions around, ignorant of the nuances of the actual syntax. You shouldnt take my psuedo code as too literally, its there to express a feeling rather than a concrete proposal. I accept at some stage the nuances/reality of the syntax can be the difference between something simple and obvious rather than irritating and awkward. But these nuances seem to hamper multiCompose, but not an operator? if 'then' is taken, 'andThen' seems a reasonable alternative.
(I'd personally be happy with a magic string with a '>' embedded in it somewhere too to mirror "=>"). |
In #517 (Dimitre’s original proposal for I think that both As the number of (even experienced) users who’ll be capable of deciphering Here’s a version that may be slightly better readable (just in case, for those who’ve never heard of function composition): (: returns ('ab', 'cd') :)
let $input := ('ab', 'cd', 'ef')
return filter($input, compose( (contains(?, 'e'), not(?)) )
(: …or even… :)
let $contains-function := contains(?, 'e')
let $not-function := not(?)
return filter($seq, compose(($contains-function, $not-function))) Similar to compose($contains-function, $not-function) |
For what it’s worth, an equivalent implementation in XQuery would be: declare function compose(
$functions as function(*)*
) as function(*) {
fold-left($functions, identity#1, fn($composed, $function) {
fn($arg) { $function($composed($arg)) }
})
}; |
if compose/chain is a function, then I'm personally open minded about left to right or right to left, I can see the arguments for both, I think this is pretty self explanatory though, and makes the right/left thing pretty obvious.
|
I agree, a keyword would make the question of order irrelevant. In XPath lingo it could be |
This is simply not good. nobody says People form their expressions as Notice that the not is leftmost. This is how we, human beings think. Putting the So, dear friends, I don't believe we could get some breakthrough in this issue... We could perhaps devise something that is the 180 degrees reflection of fn:chain, but this will also have its own shortcomings, as it is merely a reflection. Let us use our energy for something more useful, like producing an abstract base for any Kollection 😄 |
No, I think that gives an ambiguous parse. |
True, thanks. |
My comment generated a lot of discussion about whether it might be better to use an operator rather than a function. I would like to return to the original issue that I raised, which is whether we should have Dimitre has shown that you can derive the second from the first by using
|
Nothing prevents us from having a second overload for fn:chain that accepts only a single argument - a sequence of functions:
|
Nothing apart from precedent, convention, and consistency. We do not currently define any function families containing multiple functions with the same name and different arity having different result types. |
I can see that there are use cases for |
Different result types? Really? isn't as for the different arity, we actually do have many functions that have the same name and different arity (not to mention fn:concat ... 😄 ) - this is the main reason why in a named function reference we must specify the arity - like fn:substring#2 and fn:substring#3 ... and virtually any function that has default value(s) for some of its argument(s) |
@ChristianGruen Yes, and if we remember well, this started as a proposal for a multi-compose function. But then people asked for left-to-right order of evaluation of the chain of functions and this is how we got fn:chain. If we have functions with different order of (directions of) evaluation, my preference for their naming would be:
and not Personally I am happy even with just having
Contrary to this, So, let us not unnecessarily bloat the set of functions - at least we will not end up with a 5000 pages Spec. |
I agree with @michaelhkay, chain feels at least non primitive to me compared to compose. (b->c) -> (a -> b) -> (a -> c) (apologies for the functional signature but I think it makes it clearest). is confusing to people familiar with the textbook definition. BUT, most implementations I see don't call it compose or "." and use the (and I think) more intuitive (a -> b) -> (b -> c) -> (a -> c) (I actually had to write the 1st signature twice because I got it wrong) So...
"(.>) Read as "compose forward" or "and then". Use this to create long chains of computation that suggest i.e. its seems intuitive that (f .> g .> h .> i) x is f and then g and then h and then i applied to x.
|
One argument for a pure composition function is that it would be more general than what |
As I said earlier, we can have both Please, feel free to propose As for "more general" vs. "more convenient", there are both pros and cons for each - some people prefer not to have to write an additional pair of parentheses every time, other people want not to need to express a partial application. As there is no obvious "best" alternative, any argumentation for one or the other would be inconclusive. |
I am strongly in favour of adding a The real-world use-case I encountered in the past is: building up a sequence of filters, transforms and sorting based on user-input which is then applied as one function call to a data set (in a search function for example). As a result of the above a function to combine functions should allow a sequence rather than being variadic. In a variadic call one needs to know all functions at compile time. Please correct me if I am wrong here. |
If the function is variadic then you can either do |
Thank you @michaelhkay for making that clear. Is a mixture also possible as in the example below? compose($function-sequence, serialize(?, { "method": $method })) |
Yes. |
I thought I had a great opportunity to use
fn:chain
the other day, and then found it didn't do what I wanted.I wanted to negate a predicate: in pseudo-code
items-where($seq, not contains(?, "e"))
and I thought I could do this by chaining
contains
andnot
. But it doesn't work that way:fn:chain
applies a sequence of functions to an argument, it doesn't compose a sequence of functions to yield a new function.I wonder if a function that composes functions would be more useful, so I could write
items-where($seq, compose((contains(?, "e"), not#1)))
The text was updated successfully, but these errors were encountered: