This is pretty much a terrible language design idea. (Not the "out int x" change, but the whole idea of "out" in an argument list to begin with to indicate values coming out via the arguments.)
The path out of mutable argument bug-hell is not to declare that the values of some argument variables will intentionally get changed by the function in the scope where it is called. It is to make arguments immutable (like they are in the traditional mathy definition of "function"), period, and allow multiple assignment to the function call. What I mean is that, conceptually, ideally, values and state should only flow into the function arguments and only back out of the function call itself.
So for example instead of
> p.GetCoordinates(out int x, out int y);
I think the following would lead to reduced cognitive load and thus fewer bugs:
> int x, int y = p.GetCoordinates;
or even better (color me biased by Erlang/Elixir):
> {x, y} = p.GetCoordinates; # as a pattern match
Instead of a smoothly mentally-traceable flow of values into function arguments and out of function returns, we have this monstrosity of conflating mutable arguments (where values go in and then come back out changed in the scope of the function call) with this "out int x" business which seems to be indicating it intends to "push values upstream", as it were.
Obviously this can be done with the new tuple syntax, but in general I totally agree with everything you've said about 'out'. It is one of the ugliest language features ever, sometimes I wish MS would just say "To hell with backwards compatibility", and throw 'out' on the fire.
// Function definition
(int x, int y) GetCoordinates() =>
(100, 200);
// Invocation
var (x, y) = GetCoordinates();[1] https://github.com/louthy/language-ext/blob/master/LanguageE...
[2] https://github.com/louthy/language-ext/blob/daa5a10028e9637b...
Yes, like the standard library's Dictionary.TryGetValue()...
if (dict.ContainsKey(key)) return dict[key] else return null;
the only place where you can't avoid them are TryParse. And there it sort of makes sense - the value is sort of side effect
Which isn't thread-safe. So it's not always a better option.
var (first, middle, last) = LookupName(id1);
Seems like what you're suggesting, no?Based on my hazy memory of C#, if id is a struct/value type it is copied, but if it is a class type, there's nothing preventing the method call from mutating it.
Which is unfortunate, I agree.
There is, the class could be immutable.
public class Vector
{
public readonly int X;
public readonly int Y;
public Vector(int x, int y)
{
X = x;
Y = y;
}
public Vector With(int? X = null, int? Y = null) =>
new Vector(X ?? this.X, Y ?? this.Y);
}
I believe record-types are proposed for the next release of C#.On a similar note, I also really miss the ability to define const class methods (i.e. instance methods which do not modify the instance on which they are invoked). Although, perhaps get-only accessors fill part of that niche in C#.
But people should stop coding like this. For the love of all that is good and binary, don't use arguments to mutate values in the same scope. Just return them and reassign them. That way, state flow is always in via the arguments and out via the return value(s). Without this discipline, you will have a hodgepodge of "value flows" and that complexity will kill you when debugging. When new values can be spit out both by function returns and function arguments... Hold on, I have to take an aspirin
I know that a lot of OO/procedural languages permit you to do this (unfortunately). Don't!
This does not invalidate OP's point that it is terrible design. You do not need to take every bad idea in older languages in the name of familiarity.
For example, ES6 already had local functions (and a very terse form of that too using lambdas)
class C {
method() {
let localFunction = x => x + 1
function anotherLocal(a) { return a + 1; }
}
}
... arrays are normally used to represent tuples (or freeform objects), and it includes destructuring too: function f(x) { return [x, x*2] }
let [x, xdoubled] = f(5)
or function f(y) { return {y, timesTwo: y*2} }
let {y, timesTwo} = f(10)
It almost seemed to me that many of the features listed here were "borrowed" from the liberal style allowed by ES6 and TypeScript (am probably wrong, and both are borrowing from elsewhere).You're right there. Tuple unpacking has existed in even stronger form (pattern matching) in most functional or functional-ish languages for ages. Python's had unpacking since God knows when. F#, which is sort of related to ML and feeds features to C# somewhat regularly, does this sort of thing as first-class syntax.
It's sort of a trend for people who learn JS first (or only) to look at features that it's had stapled to it and then point out how much other languages are taking from it.
And I'm well aware that the direct inspiration for many of ES6's features is probably Python (and now C# with async/await, which in turn flowed there from F#)
I'm assuming some cross-borrowing is happening here due to developments on the TypeScript team, and due to much higher similarities of the specifics with TypeScript. After all, F# has been around for quite a while, yet we're only seeing this development now - so if F# is the inspiration, why did it not happen much earlier?
As for JS itself, ES6 is a great step in the right direction, and I like it a lot, though I prefer Typescript for pretty much anything when I can use it.
Also - I didn't say that you learned it first or only, just that I see a lot of praise for 'JS features' being adopted by other languages from people who very obviously don't know any better - and this only in the last 3 or so years. So I think it's a trend.
The problem with JS is you need deal with a lot of hacky legacy things too, you want try new cool things on ES6/ES7, but you need take care about compatibility, 3rd party libraries integration, etc.
IMHO, I hope something like "ES10", where they forget previous versions, create from scratch a new javascript, without worries about polifilling, compatibily, shimming, etc. Then start to evolve from there.
4 years ago I started to work more with js and then node.js until I stopped working on c#. Js has been for me a gateway to transition to other languages and get paid for that. I learned bash, python, a bit of ruby, go, etc.
If I see c# code today I don't understand what it does. Sure every version has a lot of new very useful stuff but it also introduces noise, "why he used out x instead of the new shiny out var x, he might not know about it, lets send PR".
Along the way I have learned to appreciate simpler languages that does not change that often. Where there are few or just one way to do something.
I think I read an interview to Mitchel Hashimoto (hashicorp) and he said he choose Go over all languages because it was boring.
A boring and simple language allows you to focus on the problem you are solving. A language that doesn't change for years allows people to catch up and master the tool. It also means that you can open a file written by someone else years ago and understand what it does.
IMHO js was in that sweet spot until ES6.
> If I see c# code today I don't understand what it does.
I am not surprised. The C# team should be more concerned about this. I love C#, but it is getting more complicated and arcane and therefore more unfriendly to newbies. Some of this is probably unavoidable, but some of it could be avoided. The language has to stay attractive to newcomers to survive in the long run.
As someone who has coded in both I won't deny for a second that C# is the better language, but I spend most of my time in JS these days I don't "fight against" it at all. I know how it works, and I use it accordingly.
What are you finding unnatural about the direction JS has gone in?
class Guy {
sayHello() {
console.log('Hello.');
}
}
const guy = new Guy();
guy.sayHello();
Or for practical code:
Get IDs of users named Mike db.users.fetch({firstName: 'Mike'}, {limit: 10})
.then(users => users.map(user => user.id));
The language is so popular to shit on, with absolutely no basis.I don't expect TypeScript to compile easily to WebAssembly. You'd have to write your on GC and do a bunch of other work that you get for free when you target JavaScript... and that means that there are plenty of opportunities do make your runtime worse than your target browser's runtime, at the cost of increased code size to boot.
And part of many, many languages prior to ES6.
Apologies if you thought my comment was snarky, it wasn't my intention.
With my original comment, I was trying to imply that the context in which you read about languages (e.g. which languages you are the most familiar with) can color how you perceive the news. Sadly I feel like everyone read it as an indictment against C# or something.
Anyway, my bad for mistakenly calling you out. It's a bit of a reflex after seeing so many people try to claim that their language is superior.
It's fun to see how languages influence each other.
I actually think C# is a better language than JS, but it's nice to see how they get the best features from each other.