UFCS syntax I never saw before.

Jonathan M Davis newsgroup.d at jmdavisprog.com
Mon May 21 18:53:19 UTC 2018


On Monday, May 21, 2018 14:00:55 ANtlord via Digitalmars-d-learn wrote:
> On Monday, 21 May 2018 at 11:38:12 UTC, SrMordred wrote:
> > After all this time I saw this:
> >
> > writeln = iota = 5;
> >
> > what??
> >
> > I never saw that before!
> >
> > This is interesting, there is something useful that i can do
> > with this kind of call?
>
> What the hell is this? I don't figure out why are there so many
> syntax features? It doesn't make the language more easier. Is it
> any reason to support 2 additional cases of function calling? We
> have no literal for a tuple but we call a function via 3 cases.
> I'm frustrated.

This particular example is a result of how property functions were
originally introduced into the language, and UCFS just compounded the
problem. It's also why () is optional on function calls.

foo = bar;

is treating foo like a setter property, and

auto result = foo;

is treating foo like a getter property. Heck, in

foo = bar;

both foo and bar could be function calls. As long as it's used
intelligently, this is great, because then you can treat functions as
properties. But it gets really bad when someone does something dumb like

writeln = "hello";

Because this can get really dumb, @property was introduced into the language
with the idea that it would be used to mark functions which were supposed to
be used as properties. Then dumb stuff like

writeln = "hello";

could become illegal, whereas stuff functions that were actually intended to
be used as properties could continue to be used as properties. So, the clean
uses would be left, and the bad uses would be illegal. Unfortunately,
because moving to @property meant breaking code (even code that was using
properties legitimately, since it was introduced before @property was a
thing), that meant that we had to introduce @property as doing nothing and
then switch to enforcing it later. Somewhere along the line, the -property
flag was introduced to start enforcing it, but it only partially enforced
it, and of course code continued to be written without using it. So,
actually moving to enforcing everything with @property was slow.

And then UFCS happened, and that pretty much killed the whole thing. The
problem was twofold:

1. A number of folks just got in the habit of calling functions without
parens and liked it. They would generally agree that

writeln = "foo";

was dumb, but they liked being able to just not use parens when doing stuff
like

myObj = foo;

or

myFunc(foo);

So, telling them that they couldn't do that anymore didn't go over well.

2. Once, UFCS came into the game, instead of having code like

auto result = map!(a => a / 2)(range);

you got

auto result = range.map!(a => / 2)();

and you all of a sudden had a bunch of templated functions with empty
parens when called, and since you already had a one set of parens for the
template argument, folks thought that that was ugly. So, they started
writing

auto result = range.map!(a => / 2);

So, requiring that they then use parens for that as they would have to if
parens were required when calling all non- at property functions as had been
the plan was _not_ going to go over well. It basically became completely
unacceptable to require @property for property functions, and so plans to
properly implement @property were dropped.

The result is that @property does almost nothing (it affects what typeof
does, and it affects __traits - and thus std.traits.functionAttributes - and
you can't overload an @property function and a normal function, but that's
pretty much it). Lots of us use it all the time to indicate what we intend
to be used as a property function, but it's just documentation.

Now, even if you think that calling functions with no parens is great, that
still leaves us with two problems:

1. writeln = "foo"; is still legal. Maybe we could make it so that @property
is required with free functions used as setters that aren't used with UFCS,
but figuring out a set of rules that doesn't require putting @property on
all setter functions while still disallowing the really dumb stuff isn't
easy, and it's questionable that requiring @property on setters at this
point is going to go over well. It's very annoying that stuff like

writeln = "foo";

is legal, and it's dumb, but it hasn't mattered much in practice. So,
causing a bunch of code breakage in order to disallow it is unlikely to go
over well. It would also then make getters and setters inconsistent in that
setters would require @property and getters wouldn't. How much that matters
is debatable, but it does make such a change less palatable.

2. The other issue that @property was supposed to fix was property functions
that return callables - e.g. foo could return a delegate, but the compiler
has to assume that the parens on foo() are calling foo, not the delegate
that it returns. So, even though you want to treat foo as a property that is
a delegate, you're forced to treat it as a function and use double parens -
e.g. foo()(). If @property were required, then if foo were marked with
@property, the compiler would know that foo() called the delegate and that
foo()() was trying to call the return value of the delegate. However, since
@property is clearly never going to be implemented as intended, that
problem is remains unsolved. It's been suggested that we just make it so
that @property actually do what it was originally intended to do if the
return type is callable, but that has never been officially decided, let
alone implemented.

So, the end result is - as you're complaining about - a bit of a mess. But
it's not at all clear how to fix it at this point, and with how Walter and
Andrei have become decreasingly willing to make breaking changes that don't
have large benefits, it seems unlikely that this situation is going to be
fixed. The reality of the matter is that as ugly as the situation is, it
really doesn't matter much in practice. You're almost certainly never going
to find something like

writeln = "foo";

in real code. It's dumb that it can be done, but because it's so dumb, no
one does it - just like 5[arr] is legal in C (whereas the languages that
have come after have wisely required that you do arr[5]), but it's dumb, so
no one does it. And returning callables from property functions happens
rarely enough in practice that it pretty much only comes up as a theoretical
problem to the current situation rather than a practical one. No one
actually complains about it.

So, when faced with the choice of fixing dumb corner cases that aren't
actually problems in real code and breaking existing code in the process
(since there isn't an easy way to just prevent the dumb code while allowing
reasonable code) or leaving things as-is, Walter and Andrei have chosen to
leave things as-is.

It wouldn't surprise me if Walter and Andrei make some official decision
about @property at some point in the nearish future, since one of their
priorities at this point is to clean up and complete existing features (e.g.
shared), but the most likely decision would be that @property would just be
made to do nothing and get phased out of existence, because they aren't
going to want to break valid code, and the only places that it really causes
problems are places where programmers are being dumb. But we'll have to wait
and see what they decide. I think that of all of the things they want to
finish about the language, @property is pretty low on the list, because it's
pretty low impact.

If someone wrote an good DIP on the subject, I expect that things could be
accelerated, but it's not much a real paint point in practice, and the
chances of @property actually ever meaning anything like it was originally
intended to mean are pretty much zero. UFCS killed that.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list