-
Notifications
You must be signed in to change notification settings - Fork 62
Invocation syntax for functions accepting single argument lambdas #7190
Comments
I really don't like this, it looks nice, but isn't at all regular. I especially dislike the "magical identifier" " Additionally, what would the following print? void foo(Boolean(Boolean)|Boolean() predicate)
{
if(is Boolean(Boolean) predicate)
{
if(predicate(true))
{
print("1");
}
else
{
print("2");
}
}
else
{
if(predicate())
{
print("3");
}
else
{
print("4");
}
}
}
shared void run()
{
Boolean it = false;
foo{: => it};
} |
I'm now in favor of adding a very limited form of this feature to 1.4, but by making ints.each(print(it)); The difference between my proposal, and what is proposed above, is this would be only for single-expression anonymous functions, and not for blocks. |
Oh, I see, there's an ambiguity here with respect to expression like And I think requiring:
Would undermine most of what's nice about the feature. |
I dislike this feature as you proposed, @gavinking, but there is a more disciplined version that I feel fits in more in the language. Considering #5989, I feel like persons
.filter((person) => 18 <= person.age)
.sortedBy(it.name)
.map(it.email)
.forEach(print); In addition, if there were an operator for function composition (I'll use " The problem would be when calling functions, as in |
@Zambonifofex I think what you're proposing is almost completely useless, since it doesn't reduce the token count from what I can already write today in Ceylon. And I don't see how, if Nor do I see how it's more "disciplined". It's merely less useful. |
I have pushed a prototype implementation to the 7190 branch, so folks who are interested can play with it. (There are surely bugs; I've spent about one hour working on this.) |
Types can be longer than a single token. And if you don't like " But if you really don't like my suggestion, I could get used to your idea if you introduced some indication of which function the " persons
.filter(:it.age >= 18)
.sortedBy(:it.name)
.map(:it.email)
.forEach(:print(it)); And I also don't like the fact " |
Replying to @gavinking’s comment on another issue:
Perhaps, but I think this is the most readable incarnation of the same code: persons
.filter((person) => person.age >= 18)
.sortedBy(Person.name)
.map(Person.email)
.forEach(print) In each line that does something
Yes, but we all know the typechecker is smarter than the programmer. Don’t force me to repeat the typechecker’s reasoning in my head to make sense of this code. In the end, I personally don’t believe a further abbreviation syntax for this is necessary. We already have static references for th simplest cases ( |
Perhaps, but I think this is the most readable incarnation of the same code: I agree that
I'm not motivated by a desire to avoid typing. I'm motivated by how nice I think the code for stream processing looks in some other languages that have this feature.
It doesn't have to be very complicated at all: |
Well the IDE would of course give a visual indication that |
I've pretty much finished the implementation of this. And after trying it out in my example apps, I'm sorry to conclude that I pretty much love value evenCubes =
LongStream
.iterate(1, it+1)
.parallel()
.map(it^3)
.filter(2.divides)
.mapToObj(it)
.limit(20)
.collect(toList<Integer>()); value map =
Stream
.with(*text.split(it==','))
.parallel()
.map(it.trimmed)
.filter(it.shorterThan(10))
.collect(toMap(String.string, String.size)); Stream
.concat(Stream.with(*text.split()),
Stream.iterate(1, 2.times))
.parallel()
.filter(switch (it)
case (is String) it.longerThan(4)
case (is Integer) it>4)
.limit(10)
.forEachOrdered(print); Yes, I agree that However:
So, we need to decide whether we should go for |
You've never said what you think about using " |
Well, I think it's ugly. The only motivation for adding this feature is that it makes the code look cleaner and more visually pleasing. Plus, the choice of |
@Zambonifofex To be clear, we could use value evenCubes =
LongStream
.iterate(1, => it+1)
.parallel()
.map(=> it^3)
.filter(2.divides)
.mapToObj(=> it)
.limit(20)
.collect(toList<Integer>()); But, again, all of the value of this feature is in terms of reducing visual clutter. And |
I was going to agree, until I saw this abomination:
:) More seriously, I think this is overall a good feature. There are downsides:
However, it's so concise and convenient, and as often as I have |
@jvasileff I think it's actually pretty nice that |
My point is, I'd normally prefer: persons
.sortedBy(Person.name)
.map(Person.email)
.forEach(print) but then, if we add the filter, for consistency I might prefer: persons
.filter(it.age >= 18)
.sortedBy(it.name)
.map(it.email)
.forEach(print) It's another decision (another stylistic choice). But overall, less important to me than the added convenience and reduced clutter. |
Hm. So, what is new syntax? I like syntax like:
And yes, I like syntax:
But I don't understand how it will work. I don't like reserved "it" keywords or something else. But I don't understand how new magic syntax works? |
Yes. For example, |
@MikhailMalyutin This proposed feature doesn't rely on the Instead it uses the parameter names explicitly declared in the function you're calling. |
Hm. Very interesting idea. I think new syntax is good. And I see only one problem if function have bad name, I have to use this bad name in my code. But old syntax will works - right? But I think it is necessary to implement good support of new feature in IDE. Refactoring and etc. If IDE will work good - this will be perfect feature. |
But I think next syntax will be better:
Or with blocks and return:
|
Of course. If you don't like the name of the parameter, give it an explicit name, just like before.
I've already added some support, but yes, I also need to add it to the Rename refactoring, that's true.
Well in the discussion above, we decided that implicitly-scoped anonymous functions were problematic because of nesting. They're even more problematic when you don't have a keyword like
Well that syntax won't work because the parser can't disambiguate it from a named argument list. |
To be clear, you are allowed to write: get("/", function { return renderTodos(request); }); Where the |
The thing that I find disturbing with this proposal is that it will make argument names an API — i.e. a change in nothing else but argument name will become a breaking API change. We already have this to some extent because of the named arguments syntax, so wnat's one more feature on top of that. Just wanted to point this out, that's all. |
Parameter names are already part of the API in Ceylon. Think of named argument lists. |
(Oops, sorry, you already said that.) |
But not parameter names of function parameters, right? This compiles without error: void f(void g(Integer i)) {
g { i = 1; };
}
shared void run() {
f {
void g(Integer j) {
print(j);
}
};
} |
Correct. |
Another option for This breaks with Ceylon's strict lexical scoping rules, but seems quite reasonable since There are a few significant advantages:
|
I like 'it' over declared arg names because of:
|
another reason not to restrict nested 'it': what if lambda is a single-arg lambda, but I do not use its argument? will it count as 'it' taken and you cannot use it nowhere down the hierarchy? |
I feel I have to speak for
Quoting @guai:
I think common sense can be used here. If the function is anything else than a simple construct where it is clear what is going on, explicitly declare the variable names.
Or just explicitly declare variable names. It's not the end of the world imo.
Not disallowed with
I strongly dislike having warnings everywhere in my code. I grow insensitive to them and miss the important ones. |
+1 for the |
Since eclipse-archived/ceylon#7190, an expression like `=>a*b+c` is valid: it is a function expression with zero parameter lists (see also #142). And apparently the parser will parse it as such when asked to parse this code as a (non-lazy) specifier (though I’m not sure why), whereas we actually want this to be parsed as a lazy specifier in this context. We can avoid this by attempting the lazy specifier parse first and falling back to the non-lazy case second. (Even better would be to only call the parser once, but there’s no direct rule for “any specifier”, and I’m not convinced that the alternative – turning the code into a complete example for some other rule, e. g. typedMethodOrAttributeDeclaration – would be better.)
includes an improved algorithm for assigning names to parameters of SAMs
A very common scenario in many projects and libraries is one involving functions which accept a single argument, where the single argument is a zero- or single-argument function or lambda. In these cases, it would be useful to have a syntax which allows the body of the lambda directly in the function call's braces without needing to wrap the lambda with additional curly braces, improving readability quite a bit.
The syntax would need to be able to disambiguate this usage from other possible usages of the named argument function invocation syntax.
To invent a fictional implementation as an example, one might be similar to the invoke operator but where the opening curly brace of the named argument invocation syntax is immediately followed by a colon, resulting in a new function invocation operator which opens with
{:
as an indicator that the invocation braces directly contain the body (i.e. not wrapped in additional curly braces) of the single lambda argument being supplied.Basically:
it
) only available inside the lambda could be supported for accessing its single argument, and the need for an explicit parameter list relaxed. (Nested lambdas that would create nestedit
s or shadowedit
s could be disallowed).;
at the end of the last statement or expression within the lambda block, as the ambiguity with other possible usages of the name argument syntax would not exist when using the new invoke operator. This will particularly eliminate the noise of the terminating semi-colon;
for lambdas with a single statement or expression.{
and the colon:
, surrounded by parentheses.The text was updated successfully, but these errors were encountered: