C# Pattern Matching
docs.microsoft.com
docs.microsoft.com
Right now SSDT and some ancient Linq2Sql stuff is the only thing I still find myself needing VS for.
I've found it quite problematic to reliably connect to private NuGet feeds though which is a bit of a pain.
It's faster yes, but seems to be a bit more memory hungry out of the gate than VS+Resharper.
There is one HUGE thing I miss however - the Package Manager console. There's still a few things I have to do in there and thus have to switch back to VS.
I can't do the equivalent of
update-package -reinstall
A forced package restore doesn't quite do the same thing. (I'm really glad Rider has that feature though!)
Add-BindingRedirect (would be extra handy since Rider doesn't handle redirects at all [1])
How can a IntelliJ Idea derivative written in Java be faster than VistalStudio which is native code? Both Idea and PyCharm work much slower than VisualStudio on my machine.
In my own experience in migrating servers from 32 to 64-bit on Linux 12 years ago, 64-bit was around 10-20% slower than the 32-bit build. Dont how itd fare today (no longer at that job). Even though performance was demonstrably slower, 64-bit it was due to edict from the CEO.
For what its worth, neither of the services I was responsible benefited from the extra address space. Mem usage for bother services peaked at around 750 MB.
[1] https://docs.microsoft.com/en-us/archive/blogs/ricom/revisit...
Microsoft has also pushed since VS 2008 (!) for plug-ins and extensions to use an out-of-process model. I think ReSharper got the message in summer 2019 that it might be a good idea – and incidentally, a lot of their performance comparison of Rider vs. VS comes down to "Rider uses ReSharper in another process, so the IDE doesn't slow down when ReSharper does things", while they themselves still pursued the slower model in VS. Heck, until a few releases ago ReSharper was still using the old synchronous COM APIs for querying the solution after loading, leading to severe pauses when opening a solution or re-loading it after switching source control states.
Vanilla VS is plenty fast and not that memory-hungry. In my experience it's extensions that try to sync with IDE state where a lot of the waiting comes in and in-process extensions where a lot of the memory usage comes from.
I also never liked ReSharper, the performance drop versus vanilla VS isn't worth it.
I'd also say that Rider is more stable - I've only ever seen issues with preview versions; the production versions are solid, which can't be said for Visual Studio.
I was a Visual Studio fanboi for many years before discovering Rider, so wouldn't be easily swayed - please, try it and see for yourself!
A similar thing makes me go nuts in Windows as well - in Windows 7 I pressed the WinKey and started typing immediately to find an application, never had a problem, but Windows 10 often misses the first few keystrokes after the Start menu opens. I can't believe this hasn't been fixed for years.
--
EDIT: fixed a typo
Infact, I get nervous when I'm working on a VS without it..
I don't want to NOT know why ReSharper is screaming at me!
And all of our .NET Core services on MacOS, with all my unix-y tooling.
There is “VS for Mac”, but it’s not really VS. It’s Xamarin Studio rebranded.
Rider works excellent on my Linux box at home as well, where the alternatives are MonoDevelop and Omnisharp + text editor.
Any tips, tricks, or pitfalls to avoid making the switch from VS to Rider?
Rider was what I thought I'd give a go, with the idea that if it was really terrible I'd fire up a VM with Windows and VS.
As someone who's used Visual Studio since VS 2002, and ReSharper since (probably) 2006 - moving to Rider was pretty much run and go.
There's a few minor differences, but the only one that bugged me was the regional formatting options for the debugger. I still havn't figured out how to set it to use ISO8601 or something like it for formatting dates/times, and instead it picks something based on my OS configuration.
For some reason I can’t change the position of the debugger when running and aspnetcore app and make it go back so I always have to re-run whatever request I happen to be debugging. Does that happen to you?
It also seems like ReSharper had more tips and tricks going on when compared to Rider, but I can’t imagine that would be true.
https://gist.github.com/mtone/aded2ff636cb6dd9d428cf5800f344...
Funnily, I have no clue how to setup a build script in Rider, did that in VS (Rider do run it).
This was only true for the first releases, they have been integrating common code from VS and this is one of the reasons why VS moved away from COM plugins into a .NET ones.
I'll take a "limited" language with excellent IDE support over a "powerful" language that you use notepad with.
https://resharper-support.jetbrains.com/hc/en-us/articles/20...
[0] https://docs.microsoft.com/en-us/dotnet/csharp/nullable-refe...
Doing (x != null), !(x is null), or (x is object) all become:
ldloc.s
ldnull
cgt.un
stloc.sThat also explains the switch in parameter checking from == null to is null for the default code analyzers/refactors/code hints.
Also, I don’t understand the scare over operator overloading. It’s not common and it works fine with nulls too as long as it’s implemented correctly. If it’s buggy, you’re screwed for other cases anyway, it isn’t much helpful to try to fix null checks only.
I find this advice overhyped because of these reasons.
I found this post explaining they considered moving away from the pattern, but I'm not sure if they followed through:
https://blogs.unity3d.com/2014/05/16/custom-operator-should-...
That's exactly the reason to use the opposite. In certain C# environments (like Unity game engine) the == is overloaded in a very meaningful way (when C# objects are used as representations of unmanaged objects, and they appear to "equal null" if the represented object is no longer available in the system), and using methods other than == to compare to null (such as casting to boolean) can introduce hard to detect bugs.
val shape: Option[Shape] = ???
shape.map {
case Square(0) | Circle(0) => 0
case Triangle(b, h) if b == 0 || h == 0 => 0
case Rectangle(l, h) if l == 0 || h == 0 => 0
case Square(side) => side * side
case Circle(radius) => radius * radius * math.Pi
case Triangle(base, height) => base * height / 2
case Rectangle(length, height) => length * height
}
what I noticed * scala doesn't have a way for cases to fall through, so the first cases have to each declare that the result is 0. it would be cool if we could use something in place of '=>' to make the case fall through.
* C# doesn't have structural matching, so the 'when' keyword is used more often.
* scala's pattern matching is exhaustive so if we assume the shapes are in a sealed type hierarchy then we don't need a default case.
* it's idiomatic to use 'Option' instead of null in scala, but there are lots of libraries in c# that offer option monads, so it's more a point of what's idiomatic than what's possible.In F# this is already an issue when you want to use Option/Result but as soon as you do anything with the BCL you need to handle exceptions instead, and convert to/from error cases of ADTs.
I started with C# in the 1.0 days before generics. The lack of strongly typed collections was very annoying with all of the casts involved. I used to use a template engine/code generator to create strongly typed collections. Can't remember the name of the tool... Probably been since 2002 or 2003 since I used it.
>What will Microsoft do?
>We will also aim to be done with null-annotating our core libraries when .NET 5 (November 2020) comes around – and we are currently on track to do so. [1] [2]
[1] https://devblogs.microsoft.com/dotnet/embracing-nullable-ref...
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csh...
return shape switch
{
Square s when s.Side == 0 => 0,
Circle c when c.Radius == 0 => 0,
Triangle t when t.Base == 0 || t.Height == 0 => 0,
Rectangle r when r.Length == 0 || r.Height == 0 => 0,
Square s => (s.Side * s.Side),
Circle c => (c.Radius * c.Radius * Math.PI),
Triangle t => (t.Base * t.Height / 2),
Rectangle r => (r.Length * r.Height),
_ => throw new ArgumentException(message: "shape is not a recognized shape", paramName: nameof(shape))
};Unfortunately although we are a small community, it does feel like being last on Microsoft priority list.
Microsoft please listen to feedback of your F# users namely on fslang-suggestions and fslang-design repository.
My personal wish list for F# are type classes, HKTs, macros and GADTs.
The language is already good enough.
I understand that it may cause issues to new learners, but overall, what of them do you consider bloat?
Not saying C# is bad, thats what you all seem to understand though.
Good luck to you all
Your comments are extremely condescending.
C++ is usually taken as a language with too many features but really it just has a few very flexible features and people abused those features to do all kinds of metaprogramming. As C++ has been adding more native metaprogramming idioms it has actually been getting simpler to code in.
If you want to see simplicity that stood the test of time, look at Smalltalk or Lisp.
Lisp's limited syntax allows everyone to create their own "language" and that's arguably worse than the fixed set of statements that exist in other languages. I'd even argue that Lisp is inhuman because it's brutal compared to natural languages.
This is probably why neither language is more than an intellectual curiosity. COBOL, Fortran and BASIC have also all "stood the test of time".
Not really. Definitely not compared to C# or Java. Smalltalk simply has more system-level code accessible to the user.
I've worked with Java environments that tried to replicate the visual programming features of Smalltalk. They were about 10X the size of a modern Smalltalk distribution (Squak or Pharo), had at least 100 times slower startup time and you still needed an external IDE to get anything "serious" done with them.
I have seen a function which incorporated all new language features of that language version just because the dev could. A simple for loop would have made the same work. The reviewer failed here totally. Readability, Simplicity and consistency in the code base are a thing when reviewing.
Preventing this is a duty of the code reviewer and the team.
However, only direct experience will make you reconsider.
I’ve seen a discriminated union implementation in C# the other day and was repulsed.
public static double ComputeArea(object shape) {
switch (shape) {
case Square s:
return s.Side * s.Side;
case Circle c:
return c.Radius * c.Radius * Math.PI;
case Rectangle r:
return r.Height * r.Length;
default:
throw new ArgumentException(
message: "shape is not recognized",
paramName: nameof(shape));
}
}
The when clause can be used to deal with special cases (e.g. to avoid division by 0 when a dimension is 0). public static double ComputeArea(object shape) =>
shape switch
{
Square s => s.Side * s.Side,
Circle c => c.Radius * c.Radius * Math.PI,
Rectangle r => r.Height * r.Length
_ => throw new ArgumentException(
message: "shape is not recognized",
paramName: nameof(shape))
}; public static double ComputeArea(object shape) =>
shape switch {
Square s => s.Side * s.Side,
Circle c => c.Radius * c.Radius * Math.PI,
Rectangle r => r.Height * r.Length,
_ => throw new ArgumentException( message: "shape is not recognized", paramName: nameof(shape));
}[0] Roslyn Analyzers: https://docs.microsoft.com/en-us/visualstudio/code-quality/r...
In fact, `where` isn't a reserved keyword, either--you can have an identifier named `where`.[1]
To an existing C# programmer, `where` makes a lot of sense, since it's used for matching elsewhere (e.g., LINQ).
[0]: https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...
[1]: https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...
They don't let backwards compatibility keep them from introducing new features and syntaxes, which is quite nice. Most languages that want to avoid breaking backwards compatibility seem too hesitant to introduce new language features.
In 2020 Pattern Matching is just a elementary feature everyone wants to have in all general purpose language. Like async/await. Or LINQ. That is just 101 for languages from now on.
What i vaguely remember from my OCaml times, I think working with lists is the major gap they have. And that is non-trivial.
Is the ability to build class hierarchies not the ultimate reason to use C#, an Object-Oriented language? Which one of the two is idiomatic?
Great, now we have code littered with methods that take objects as parameters, so we have no clue what to actually pass to the method?
C# is a fantastic language but I feel increasingly lost with the barrage of new features added to it.
There are several very good reasons that the inherited wisdom about OOP includes such phrases as "prefer composition and delegation over inheritance".
There are some cases where class hierarchies are actually good, but they're far rarer than most would suspect. The Liskov Substitution Principle is less of a guide as to how to use inheritance as much as it is a guide as to when you should not be using inheritance at all.
And to address your point: the only time objects with class hierarchy and subclases should be accepted as arguments to methods is precisely when the LSP holds. Otherwise, you're gonna have a bad time.
I don't envy any newcomers to the language though, there is so much to take in.
1) Always prefer composition and delegation to inheritance.
2) Always prefer interface inheritance to implementation inheritance even if in the short term it results in code clones.
3) If you intend to have multiple subclasses that adhere to the Liskov Substitution Principle, it is always better to have IFoo and X implements IFoo, Y implements IFoo... than it is to have abstract class Foo where X, Y extend Foo. You can still do that, behind the scenes, but your public interface should whenever possible be nearly entirely interfaces and concrete classes.
4) Inheriting the body/behavior of methods is seductive but ultimately only a poor excuse for not delegating that will create maintenance headaches down the line.
But you are correct that method overriding and pattern matching partially solve the same problem in different ways. C# is not really a pure OO "one way to do it" language anymore, it is a multi-paradigm language, for better or worse. Arguably it have been since anonymous function was added.
In the functional programming community, this is referred to as "the expression problem".
To understand this problem properly you must first accept the idea that every object (or class of objects) in an OO context is essentially an interpreter defined by how it responds to sequential messages (methods).
The crux of the expression problem is the tension between adding new operations over a type (i.e. new methods on the base class of a hierarchy) which requires implanting that method for every existing subclass... and adding new types (new subclases) over which an existing operation (method from the parent class) applies, which requires implementing every existing for the new type (subclass).
There are some "clever" approaches to "solving" this problem, but they only really make sense for types that look and act like ASTs. Despite many complex programs essentially being interpreters for some internal "language", this isn't as useful as one might hope, and the machinery necessary to "solve" the problem relies on a lot of compiler and language extensions.
If you want an example of (uh) "solving" the expression problem, the canonical starting point is Wouter Swierstra's "Data Types a la Carte" paper.
The ultimate reason to use C# is because it's a modern managed language with an excellent base library, excellent tooling and excellent debugging experience.
Object oriented hierarchy is not a value in itself. Often it's an antipattern. I'd day 80% of time you are off much better by cleanly separating your program to "data" and "algorithms", and manipulating data as far as possible in immutable fashion. I.e. when mutating an array, don't overwrite elements, rather copy the values to new array. This will be easier to understand, and likely faster due to how memory and caches work on modern platforms. Etc.
"Which one of the two is idiomatic?"
Idiomatic is a weak argument when talking about programming. Either the problem is trivial, hence all you have left are to discuss trivialities, or you don't understand the problem and are therefore discussing things of very little consequence.
Writing maintainable, understandable code is important. But "idiomatic" gives the idea that there is some higher level ultimate truth on what is always the best formulation for each and every problem.
If you are writing boiler-plate code, then yes, best pattern will emerge eventually. But then it should be self evident which is best way to move forward. Hence, 'idiomatic' once again loses it's value.
> Writing maintainable, understandable code is important. But "idiomatic" gives the idea that there is some higher level ultimate truth on what is always the best formulation for each and every problem.
This is the sentence that really rings true. Idiomatic is often dogmatic, especially in the OO language world. As far as I am aware, there's very little research backing up the GoF doctrine but it's often rattled off as "what you should be doing" and idiomatic.
As someone who 'saw the light' on this later in my career I have since moved to using functional programming (in C#, because the project I work on started life as OO and is a never ending huge web-app); Mathematics has a lot more to say about correctness than the imperative/OOP world of idioms, and I find I can trust my code much more than in the past.
I needed to create my own Base Class Library to make it happen [1], which has been a labour of love, but has fundamentally changed our multi-million line code-base for the better. But, obviously it has to fight against the baked-in mentality of devs in the OO/C# space by providing a non-idiomatic solution.
The next step on the evolution will be category theoretic approaches (I believe), languages like Statebox are already starting down this path. How languages like C#, that still have the legacy of the OO world baked into its framework and grammar, will cope with this evolution long-term remains to be seen. However, right now I think they're just about getting it right. And C# especially is still one of the easiest languages to be productive in, with a world class tool chain.
I can't list how many time's I've ran into this kind of issue when trying to explain to a peer/intern.. (Only know I probably messed up a bit now) And HN links like this just remind me that I would love to take a 'bus mans holiday' just to catch up on the framework.
This is a huge plus to the benefits of sending employees and me (please send me) to those release conferences. 'New dotNet Core coming ?' : I want to be there, but I have to live in Europe and not able to take holidays, so the stream is saved; I'll watch it later (I rarely do). :(
Whatever about patterns and higher level issues, knowing the framework is also damn key important and often overlooked as a given. Really knowing the framework, all the way down to the compile time really offers some incredible results, I have been fortunate to work with some who really knew the framework versions that we were in and it was always so much fun to put bets on how much time that team would save from ours and other teams from even just a light refactor. Most important is that developers should know the framework, not because of high time savers, but so that nobody is re-writing the wheel (intentionally did not say : 'not reinventing')
I wish I had the time to always dig through the release notes of the latest framework abilities, not to mention 'Core'.. I might bring this up as a task for the team, but does anyone genuinely have good suggestions on how to drill into fellow team mates that its important to check up on framework functionality..
I really feel kinda insulting to say: 'maybe google it to see if there is a simular issue that could shine a light?'
God bless HN.
Edit: ReSharper is my best colleague.. I'll copy an important note from below:
>Note : I am not fighting against tools, just that sometimes, me included, the tools make us not go and search why resharper is screaming: 'you're an idiot'!
I'm guessing you use Visual Studio and don't have ReSharper? If so, I have 2 recommendations to make:
1. Get ReSharper! When it sees code that could benefit from language features, it will suggest it
2. Get JetBrains Rider - seriously, try it - it's an alternative IDE with all the ReSharper stuff built-in. I personally find it a better experience than Visual Studio, and it makes my laptop fans whine far less!
2)Rider though.. It's new to me.. I'll certainly give it a go.
But one of my main points was : 'Hey, we work with this language, we should know it'
Get me ?
Note : I am not fighting against tools, just that sometimes, me included, the tools make us not go and search why resharper is screaming: 'you're an idiot'!
I find it helpful to read over them even when picking up a new language / framework to see how it has evolved over time.
so this is valid (although useless) c# 8:
public static bool Invert(bool value) =>
value switch
{
true => false,
false => true
};
I worry though that the "switch" syntax is becoming a big complex monster, and can be used to make utterly unreadable code, rather than simplifying as promised. We'll probably see both in practice.It might have been nicer if they could have used a new keyword e.g. "match" to carry the new syntax, rather than overloading "switch", but that would not be backwards compatible with C# 1-7, as "match" was not a reserved word. (2)
Hopefully switch the syntax that supports these various cases doesn't get too tricky.
1) https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csh...
2) https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...
I have yet to meet a developer who gave C# a legitimate try and then decided it was entirely not for them. That said, I only personally know developers who are working in the realm of B2B application development, so perhaps there are other incompatible use cases that I am blind to from my current perspective.
Using OpenCV from .NET is painful, but from Python it feels native.
I don't mind Python, but now that I've gotten a taste of ADT's and non-nullable types, writing Python feels like a kludge.
Even more pleasant (imo) is installing VS Code and .NET Core SDK.
Does anybody have examples of real-world use cases that take advantage of this that couldn't (or shouldn't) be solved in a more OO way (Visitor, Strategy, Template Method, etc)
Rust's enumerations (and others) enforce that all the enumerations are dealt with in the match (a superclass of switch, mixing metaphors).
Switching a simple switch with a strategy pattern can make code far more complex and hard to understand. Switches are simple to code, simple to read, and easy to change.
Because the gods of OO decreed as such and now the logic must be distributed across a dozen classes/files.
Notice the language "that couldn't (or shouldn't) be solved in a more OO way", their goal is to create OO code, not simple to read and easy to change code.
When it is used, it's very nice to have help from the language. I much prefer that to some kind of purist, "they shouldn't do that so we won't help". I certainly miss having type guards when I work in Java.
https://docs.microsoft.com/en-us/dotnet/standard/design-guid...
Unfortunately, real-life use scenarios are verbose and involved and require a lot of background context that detracts from just demonstrating the syntax. This is just demonstrating the syntax. Standard patterns of software design still apply.
That's clearly a use case for interfaces and type-level programming.
They could have made a better and shorter case with error handling logic.
I guess in a dynamic functional language you'd probably avoid doing this type of contact-oriented stuff, but you'd rather pass a callback at the last point rather than check type of what you got passed.
And that would have been a good example! contrary to the one in the docs.
Pattern matching for checking fields of things of the same type is a good use. Pattern matching when checking your parameters type? bad.
> The nice thing about it is the compiler can assert that your match is exhaustive
If you pass a new Shape object for which you forgot to implement a new case in the pattern matcher, you just get the default case, which is wrong, but the compiler can't know that. Now have the function argument ask for something that implements IArea interface and if you don't implement getArea() in your new Shape class, the compiler will know.
It's less abstract, simpler and potentially much faster, it's only considered bad by people that think writing OO software is the goal and not a tool to use.
https://github.com/Matthew-Dove/ContainerExpressions#eithert
Once again - thanks for your contribution to my learning!
One thing that I like about my lib is that is serializes to json pretty well, and i added some more special-case variants such as accumulative errors (AccResult) and some applicative lifting operations.
Given that probably 90% of code is written by C or D class programmers, every new feature adds cognitive load that makes it less likely that the median programmer (who almost certainly works offshore and who likely doesn't have enough English skills to process the tutorials) will write bug-free code.
Once upon a time MS understood that highly educated university graduates are only a small percentage of the programmer market and kept that in mind.
Looking at the new language changes, that's been forgotten in order to please the programming elite.
Spare a thought for the poor souls who curse every time Microsoft adds a new blade to the Swiss Army knife of C# because they know they'll be the ones mopping up the blood of the countless programmers who've cut themselves on the new feature.
Nice work, C#!
Wish more language designers would respect code authors like these designers do.
It's all of our good fortune that it's in YC's interests to fund HN just as it is, with the moderators' primary job being to keep it interesting and hopefully keep the community happy. If HN were a startup, we would have to play the growth-hacking game. If it were the media property of some larger corporation, some manager would eventually put a monetization squeeze on it. Either of those tactics would ruin this place, whether or not they succeeded.
YC doesn't need us to do such things because a happy HN is the most valuable-to-YC HN. It's basically an accident—a dual accident of how YC's business works and how things historically developed—that we ended up in that spot. It's a special position to be in, and our first responsibility is to preserve it. Hence our motto, Move slowly and preserve things.
By contrast, with a community language where these decisions are made in public, by committee, and perhaps by a team of volunteers, it seems like things are always just a bit more strained. I'm sure some of the more famous PEPs took a huge personal toll on GvR, and there's no doubt that they caused a lot of high emotions. I see similarly troublesome patterns in Nim and Scala, where it would seem that "trying to avoid too much conflict with people who are forcefully communicating strongly held opinions over a medium like the Internet where it's difficult to modulate emotions" can be a real factor in the decision of what language features to include and how. And, in that kind of environment, it's probably particularly difficult to keep the Internet, with all its . . . Internetiness, at bay for long enough to really make sure you've got all the details dialed in right. Much easier, I imagine, to go for the punt and get the whole business over with.
F# for example is set loose of these constraints.