New Features in C# 7.0
blogs.msdn.microsoft.com
blogs.msdn.microsoft.com
Out parameters are going to make some things much nicer. I can certainly see local functions being useful as well.
Out of all of it, I think the thing I'm most excited about is pattern matching. Having used F# for a short time, that was the biggest stand-out feature; it really does make some code much more concise and expressive.
I love C# but lately I've been using Python for my quick-and-dirty work because in general I find it to be more expressive. It seems like C# 7 might make me reconsider.
Anyone know why it doesn't start with Item0?
At the time, we picked one based indexing because we felt it sounded more natural when spoken allowed (mirroring saying something like: This is the first Item in the tuple).
I would expect that the design bled over into ValueTuple, which looks very similar to the reference version, except, of course, it is a value type.
The other one we'll never see: Non null references as a default, optionals as a special case.
I'm all for C# getting more expressive, but what it calls "Pattern Matching" simply is not the same thing as real honest-to-goodness Pattern Matching.
C# "Pattern Matching" is little more than destructuring, or switch, on steroids.
Much like how the compiler assists you when writing subclasseses so that you've implemented all the required methods, real Pattern Matching (F#) gives you the same guarantees from the compiler that you've handled all the possible cases appropriately.
This makes real F# pattern matching much more useful than "switch with destructuring", and helps you grow your programs in a safe way.
The C# way has all the same problems as the switch statement, which gives no feedback about what cases you've handled.
So, what we're seeing here is not necessarily the final stage. It's what we get in this release of the language, but more will likely follow.
I'm using LinqPad for all kinds of one-off scripting jobs and loving it. I was trying to learn PowerShell for that task but I've finally given up on that. PowerShell has some great features, but the overall chaotic syntax just kills me.
Either way, neither of them run "true CSX files", but their own homegrown format that predates csx. But it has made me hopeful for the future of C# scripts.
It still needs a project file, but it has a nice command line interface, which means you don't need VS.
But I'll probably still keep using LinqPad anyway.
Still I agree with you and fear that this will result in developers using out parameters other places because - "why not?".
Out parameters are bad, even with the improvements in C# 7. They're simply the Wrong Way to do multiple return values.
I agree, but I fear that even with the new features, the according APIs will be missing for a considerable amount of time. It constantly surprises me is that MS has never bothered to update their old APIs with more modern methods; especally where out parameters are used for optional return values.In almost all my projects I have extension methods like this:
public static V TryGetValue<K,V>(this IReadOnlyDictionary<K,V> dict, K key, V @default = default(V))
{
if (dict == null)
throw new ArgumentNullException(nameof(dict));
V result = default(V);
if (!dict.TryGetValue(key, out result))
return @default;
return result;
}
There is a considerable number of existing methods that would lead to sooo much nicer code if there would be some versions using modern C# features. In the case above, you can still use the original TryGetValue if there's no usable sentinel value you can use to distinguish "not found" cases from success.There are also a lot of old methods using Type arguments and requiring a cast where a generic alternative is really overdue.
Edit: Of course, some Optional<T> type would also be nice for the dictionary example, but since C# has null-coalescing and null-conditionals since version 5/6, the above example suits almost all my use cases, and can be used in a Maybe Monad-style way anyway.
p.GetCoordinates(out int x, out int y);
vs var coordinates = p.GetCoordinates();
out parameters should be deprecated, not enhanced.Patterns are OK, but switch statement with patterns...why? I'd much rather do something like this:
class ShapeHandler()
{
public void Handle(Circle c)
{
...
}
public void Handle(Rectangle s)
{
...
}
}
The argument for Tuples:> Custom-built transport type for every method: A lot of code overhead for a type whose purpose is just to temporarily group a few values.
I disagree. Before you know it, that 'temporary group' becomes a first class citizen and deserves its own type. If it's code overhead, I'd rather the IDE/tooling help me with it than it be built into the language.
Overall these changes make the language more expressive which is good. I just fear they will be abused more often than not.
var x, y;
p.DoSomething(out x, out y);
Where this becomes useful, I think, are places where you can't avoid using an out parameter. Now, you can choose to throw away or use the out parameter without explicitly declaring it. var isNumber = Int32.TryParse(number, out x); public static class Parse
{
public static Option<int> TryInt(string str)
{
int value;
return Int32.TryParse(str, out value)
? Some(value)
: None;
}
}
var x = Parse.Int("123").IfNone(0);
var x = Parse.Int("456").Match(
Some: x => x * 2,
None: () => 0
);
Parse.Int("456").Iter(Console.WriteLine);
https://github.com/louthy/language-ext/blob/e6ff382a1752c7de... using System;
class Foo { }
class Bar { }
static class Program {
static void Example(Foo l, Foo r) { Console.WriteLine("Foo Foo"); }
static void Example(Foo l, Bar r) { Console.WriteLine("Foo Bar"); }
static void Example(Bar l, Foo r) { Console.WriteLine("Bar Foo"); }
static void Example(Bar l, Bar r) { Console.WriteLine("Bar Bar"); }
static object Create(string arg) { return arg == "Foo" ? (object)new Foo() : new Bar(); }
static void Main(string[] args) {
dynamic l = Create(args.Length > 0 ? args[0] : "Foo");
dynamic r = Create(args.Length > 1 ? args[1] : "Bar");
Example(l, r);
}
} public static Dictionary<(long id, string firstName, string lastName), (string shippingAddress, DateTime date, List<(long itemId, string itemName, decimal price)> items)> LoadCustomerOrders()
{
// ...
}
in your codebase :)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).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.
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.
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.
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.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.
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.
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.
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.
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.
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#.
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.
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.
SomeReallyLongTypeNameICantQuiteRemember data;
if (dict.TryGetValue(id, out data)) { ... }
Now reduced to: if (dict.TryGetValue(id, out var data)) { ... } error_type function(out data, out data2, ...);
instead. Given that C# has proper try/catch and other mechanisms of error handling I'd hope that this pattern won't repeat itself.Additionally, it allows restricting the scope of the variable, like in the TryParse examples. The output variable in something like "if (int.TryParse(someString, out var i))" is now scoped to the if block. It cannot be used in the else, nor inadvertently later in the parent scope.
This is now valid and returns a tuple with named values:
public (string city, string state) GetCityAndState()
{
return ("Lake Charles","Louisiana");
}
And you can get the return values without writing "Tuple<" once.A tuple doesn't have behaviour, inheritance, encapsulation, or data hiding. Tuples are language-agnostic.
An OO language has little choice but to represent tuples as objects, but to say that tuples are objects is no more true than saying that a user's account is an object, a TCP connection is an object, or a window on screen is an object - it mistakes representation for referent, it confuses the map with the territory.
> ut to say that tuples are objects is no more true than saying that a user's account is an object, a TCP connection is an object, or a window on screen is an object - it mistakes representation for referent, it confuses the map with the territory.
No, it makes the map useful to navigate the territory by accepting the abstraction of objects as a useful thing; there's a reason to say "everything is an object", it's a powerful ability. I'm a Smalltalk'er, everything literally is an object with methods even if/while/for/foreach statements are modeled with objects.
That you find it confusing or meaningless doesn't matter to me, it's my reality tunnel and it works well.
This conversation, such that it is, is over: simultaneously insulting your co-conversant while explicitly and resolutely staying within your own perspective means there will be no communication, by your will. Good luck with that.
What do you think of the Try pattern, i.e.:
Result res;
if (TryThing(out res)) {
// Do stuff, use res
)
I always thought that was a good use for out, and C# itself uses it for TryGetValue in Dictionaries for instance.I'd call it a valuable exception to the rule.
Couldn't the generic parameter of Some<T> be a nonboxed value type, though? Some<T> could even be a struct, to avoid object allocation.
Edit: although that would get boxed so it could fit in a Maybe<T>, so yeah, I see what you're saying!
dictionary.WithValue(aKey, value => DoStuffWith(value));
The dictionary knows whether it has a value or not, I shouldn't be making decisions with if's about a dictionaries data when I can just hand some code to the dictionary and let it do the right thing.Your other example.
if (TryThing(out res)) {
// Do stuff, use res
}
I'd rather a simple extension to Object enabling this on any value including null... TryThing().IfPresent(thing => thing.DoStuff());
I'm an old Smalltalk'er, we're accustomed to building such control structures into the class library rather than trying to use an if statement for everything. Once you have a clean syntax for lambdas you have the ability to create a better experience than constantly writing procedural code using the common if/while/for/foreach constructs. In Smalltalk we'd do something like... dictionary at: aKey ifPresent: [:value | value doStuff ]
That's much better than asking if the key exists with an if, or accessing the dic at a key and doing a null check with an if statement on the value.C# still lacks quite a lot in comparison to Smalltalk, but it's slowly getting there year by year.
Just yesterday I fixed a threading bug caused by some semantics around passing by reference and locking.
(string first, string middle, string last) LookupName(long id)But maybe it is just the nature of the [C#] language. Still, I think this looks more readable than if-statements.
EDIT: Maybe it is just a bad example of pattern matching, the one they put on that page. I'm betting pattern matching works on the added tuple type. That would be pretty handy.
https://github.com/Jagged/OneOf
I've got some more performance improvements and code cleanup in my stash RN I've not quite finished up yet.
I don't think they'll truly catch on until universities start teaching FP.
That alone though may also not suffice though, all that gets the ecosystem is super-generalized hyper-abstract "libraries written by and for PhDs".. not that I'm anti-intellectual I don't think, but if we're talking about "taking off".. programming always took off with "the tinkerers" first and foremost. (Sure, some went on to get hooked on Category Theory but certainly not most!)
I guess FP needs the kind of "OOP summer" hype we got in the mid-90s to mid-2000s. And I kinda feel that's in full swing just not coming into full view just yet.
That was true in Portuguese universities in the early 90's and it is still true nowadays.
Back then we had introduction to Lisp, Caml Light, SML and Prolog (LP).
The advantages are in the idioms, immutability by default, null not being legit value by default, emphasis on correctness, advanced type system etc.
The issue is often that people doing C# won't gain enough FP practice to see where F# brings most advantages, try to use it as a OO language for really short time and ditch it because tooling and concepts are not the same as what they are used to.
I'm confident though that adoption is rising and that few years from now most .net shops will get exposure to some amount of F# code, and that people who have been doing C# for so long will eventually pick up any functional language, which in turn will make F# really appealing to them.
I'm happy C# is supporting constructs such as local functions and some basic pattern matching (although type tests are probably the worst use case of pattern matching as seen in ML languages), but I'm also concerned the language is evolving toward more and more complicated syntax and rules (see all the kinds of scoping rules for variables and switch statement) and so little things geared toward correctness (readonly locals, non nil references?).
C# focuses on alleviating developer pain, but sometimes making the choice of enabling to do the wrong thing more readily.
Are you familiar with the expression problem? [1] It's a problem that comes up a lot when you have a bunch of algorithms and a bunch of related values in a data structure, and you want to apply the algorithms to the values. If you have a fixed number of algorithms but keep adding new types of values, it makes sense to use virtual methods for the algorithms; if you have a fixed number of types of values, but keep adding new algorithms, it makes sense to use switch statements (ideally with completeness checking), or pattern matching (if available).
The second case is where the C# feature is useful, and it does not produce terrible code in the long run.
If you have a switch with 3 or fewer cases, use if/else. If you have a switch with more cases, a hash table is going to wind up with far less code, and set you up for polymorphism later if you decide you need to go that route.
I disagree that hash tables will end up with less code; the dispatch mechanism for a hash table will be significantly longer than a switch, and the compiler can create a hash table for dispatch (using e.g. a perfect hash function). A switch statement will both be faster and easier to understand and read than storing lambdas in a hash table.
For pure readability, languages with good support for hash literals and lambdas may have the edge, but they'll also encourage dynamism that will hurt both simplicity and performance.
While this isn't possible in the general case, it might be possible to write a rule in FxCop (or other static analysis tool) to do this kind of checking for e.g. private or internal base types, or to at least check for all the derived types of the current assembly (or solution assemblies.) It also looks slightly less ugly than else if chains, which seems to be a driving factor behind a lot of C#'s newer syntactic sugar.
As in truth tables, where the conditions are encoded in cascading if / then if / ... blocks vs captured in a precomputed 2D array.
I forget what we call that style of programming. It's mentioned in Code Complete. Though it's not a common idiom.
The fact that tuples aren't so painful to write and declare is going to make me use them much more - happy about that.
Maybe I just need to move on with the times. I still see a lot of value in OO, but it's clear it's not at all in vogue. I am glad that functional ideas - particularly immutable data, and anonymous functions - have come into the mainstream. But I don't feel the need to throw the baby out with the bath water.
It's kind of a pre-condition (though not in the current initial limited variation) for the introduction of Algebraic Data Types (as F# & co. have them), which I'd still be missing in C# if I had to get working on a C# codebase (might happen again soon, I just don't get any Haskell enquiries in my freelance channels =)
I just don't see why you want both inheritance and ADTs in the same language. But it's entirely possible I'm missing something, I'm an amateur PLT enthusiast at best.
That's why I suggested "optional" ;) given how powerful .net tooling is, there'd be a
dotnet --flipformat CodeFile.cs
command that IDEs/editors would invoke from a button/menu.(I like Haskell's approach here, "layout-by-indentation first" but semis accepted for multiple-things-on-one-line and you can actually selectively have blocks in braces where the default indent-layout is inactive as your blocks are now brace-layouted.)
I was just half-joking with the above, it's just that once you get enough FP sugar in a language to actually be able to effectively write "FP style" that the desire for such syntax evolves, to wit: EcmaScript's fat-arrow functions or C# similar fat-arrow "expression-bodied methods/properties"
I wonder if there will be a way to do something like case classes in C#.
https://github.com/dotnet/roslyn/blob/features/records/docs/...
Now, across compiler vendors and versions it is hard
Having a good CI is a must though but it's not nearly as hard as porting an old codebase backwards.
One of the things that attracted me to Java was that I finally could write something where I could use 100% of language features and actually use it everywhere.
C++17 is around the corner, yet most embedded, mainframe and commercial UNIX vendors are between C++11 and C++14, in the few cases where they already support anything from C++14.
Then there are the UB and implementation defined differences across those compilers.
For example, back then aCC wasn't even fully C89 compliant.
Of course when one uses the famous trio (gcc, clang, msvc) the world gets simplified a lot.
A very mature language, rapidly evolving, with an enormous volume of well proven libraries and tools, or a cutting edge, barely production level (with a breaking changes language), tiny library new toolset. And you question why someone doesn't just transition? Is that serious?
Oh no you didn't.
There's an argument to be made in Rust vs C++ but to my knowledge there hasn't been a breaking change in Rust since 1.0 and there's been a ton of work to keep it that way.
The key is, as you said, you aren't even sure there were ones. That's how we like to keep it, and that's part of where all that work goes. The rest goes into making sure nothing breaks accidentally :)
Trivial example: shortly after 1.15 was released, a new API was found to have a soundness hole, so a breaking change was made to fix it in 1.15.1.
I love Rust, and I've been trying to get a hobby project written in it - but it really stinks that I can't just rely on my distribution to keep dependencies up-to-date like I can something written in C or C++.
Sure if u write game engines, do it in C/C++. Embedded devices? Probably a good choice.
I work at a startup, and we only have a few devs. I share my C# code-base across the client/server/mobile platform and even cross-compile it to a few other lauinages in between. If i need speed, i just extern out to a C/C++ dll i need and get it (never need to). Or integrate a microservice in eralang.
C# is just like C++, to write fast code, u need to understand the hardware. Its just C#, the hardware includes the garbage collector.
Bingo. Those two things are exactly what I do professionally and as a hobby.
Usually we switch stacks when we switch customer, on average every six months or year.
Sure there is a kind of adaptation period, like the first week, then is back again to full speed.
I also use both on personal projects, so sometimes I use the stack that I currently don't on the project.
There are also mixed projects where the frontend is a native desktop application written in .NET, with the backend done in Java.
Basically, it is no different of mastering several human languages at writing, reading and speaking levels.
[1] https://github.com/consoleau/kotlin-jpa-specification-dsl
C# runs just fine on Linux, using .Net Core.
Perhaps the C# of tomorrow will look like the Haskell of today.
Control.Arrow?
Idris?
"Also there is support for the much more difficult higher-order polymorphic types like Monad<MA, A>. LanguageExt 2.0 provides a fully type-safe and efficient approach to working with higher order types. So yes, you can now write functions that take monads, or functors, or applicatives, and return specialised types (rather than interfaces or dynamic results). So intead of writing a function that takes an option, you can write one that takes any monadic type, bind them, join them, map them, and return the concrete type that you pushed in."
[1] https://github.com/louthy/language-ext/releases/tag/2.0.26-b...
DOT is some great math, letting them bring simplicity and a ton of high level features to the compiler with very little cost.
Also, ValueUtils (https://www.nuget.org/packages/ValueUtils/) is nice for making value types.
*or one of the many alternatives (https://www.nuget.org/packages?q=discriminated+unions) e.g. SuccincT, DiscU
Although the CLR does allow for them.
Global functions can be emulated:
public static GlobalFuncs
{
public string Greet(string x) => $"Hello, {x}";
}
Elsewhere: using static GlobalFuncs;
public void Test();
{
Greet("HN"); // "Hello, HN"
}But, as much as I love C# (I changed jobs so I could work in it) this just has a pungent code smell to me:
p.GetCoordinates(out var x, out _); // I only care about x
Ug, some random underscore? I'm no language designer, but something maybe could have been done better here? It just looks fugly to me.
At the point your writing code you don't care about, something might be wrong.
If you bound it to a variable and didn't use it, it'd be the same as declaring an unused variable.
> Tuples are value types, and their elements are simply public, mutable fields
> Should tuples be mutable or immutable? The nice thing about them being
> structs is that the user can choose. If a reference to the tuple is
> readonly then the tuple is readonly.
> Now a local variable cannot be readonly, unless we adopt #115 (which is
> likely), but that isn't too big of a deal, because locals are only used
> locally, and so it is easier to stick to an immutable discipline if you
> so choose.
> If tuples are used as fields, then those fields can be readonly if
> desired. public void Test()
{
// Doesn't work
Func<A, string> lam = (A a) => a.ToString();
// Works
string LocalFunc<A>(A a) => a.ToString();
}
Also I think if the local function doesn't close over any variables in the containing method then there's no additional memory allocation, whereas the lambda itself must be allocated.And the reason why GOSUB was an anti-pattern was not because it was local, but because it was so low-level. It was not really a function call - it changed the current instruction pointer to a new statement in the same function (so you didn't get a new execution frame etc), and pushed original instruction pointer onto its own internal stack, from whence you could then return to (effectively, GOTO) it with the RETURN statement. Because it had no execution frame, it also had no provision for passing arguments. And because it was the same code, there were no boundaries - you could flow into the section of code that was meant to be GOSUB'ed into without the use of GOSUB, and you could flow back out without RETURN. E.g.:
foo = 1
IF foo THEN GOSUB bar
PRINT "No GOSUB"
bar:
PRINT "Inside bar"
IF foo THEN foo = 0: RETURN
PRINT "No return"
On the first run, this will do GOSUB, you'll see "Inside bar", then the flag is reset and we return to the point where GOSUB is invoked. But then the natural code flow will pass the "bar" label, and all that code is executed again, except this time it doesn't RETURN, but instead just keeps flowing on.So, basically, there's an utter lack of structure here. There's no matching of GOSUB and the corresponding RETURN - indeed, there may not even be a corresponding one, or there may be many, and the same RETURN can be reached via GOSUBs through different labels (and will return to wherever the GOSUB came from). If you RETURN without any GOSUB on the return stack, that's a runtime error, not a compile-time one. If you GOSUB without a RETURN, it's not an error, but you will eventually overflow the stack if you keep doing it. And so on.
So, it's a very messy mechanism that dates back to pre-structured days of BASIC. That's the only reason why it was discouraged why BASIC got SUB/FUNCTION.