Hacker News new | past | comments | ask | show | jobs | submit login

As someone who's more than happy with C#, I'm wondering if there's a world out there I'm missing but I can't really connect with what you're saying.

1. "Strong and intelligent"? "Convenient"?

2. "Sense of design"? My current side project includes a web scraper which uses the following open source projects: AutoMapper, CsvHelper, HtmlAgilityPack, Twitter Bootstrap, jQuery, Modernizer, Moq, NLog, MongoDB C# driver, PetaPoco database driver, Rx extensions, knockoutjs and structuremap. 8 of those are .NET specific. Because of their excellent design, I have probably no more than about 30 lines of code hooking up to them (where I use interfaces) and maybe no more than 100 lines of code total utilizing them directly. Yet they conceal YEARS worth of work I would have to do myself.

3. Have you ever used Resharper? I've never heard of anything equivalent on any platform for any IDE (except for IntelliJ, developed by the same company, Jetbrains).

4. I'm fine with Windows. I find both Linux and Mac less developer friendly. I'm fine hosting my services on Linux and interfacing with them from C#, though, because it's a great server operating system. I've been using computers for 20 years and I'm happy to be away from the command line 99% of the time. What do you like about it?

5. Git Extensions + Git Source Control Provider are a completely integrated and free Git experience on Windows.




1. In Haskell and ML, I don't feel like I spend my life Listing<List<T>, X<T>>, and I have type classes. In Python and Ruby, I don't have to specify types. This lets me do all kinds of convenient things.

I don't think it's far out to suggest these sorts of advantages, and I am 100% not interested in rehashing a flame war that's been had a thousand times over. Please allow that these are my judgements, and I hope you don't need to share them to see that many people feel that way.

2. That's nice? I found C# projects to have a poor sense of design. I'm not sure how you expect me to defend an aesthetic decision.

3. I'm glad you like Resharper? I didn't. I'm a vim guy.

4. I'm glad you like Windows? I don't. I see what you mean, as long as you stay in the MS womb, it's a cozy development environment. I tend to like open source software, and it lags behind very seriously in that department.

5. As I said, I last developed C# in 2009, and I thankfully haven't had to touch Windows since. I'm sure git is nice now, but I suspect that Windows is still a second class citizen for many software projects. Git is just the one that annoyed me most back then.

It seems to me that we simply have different aesthetic criteria.


as long as you stay in the MS womb

You don't need to debase an otherwise great conversation with incendiary language like that.


I certainly didn't write it to be incendiary, and I don't read it that way?

I'm not going to go back and edit it, but I'll ask you to give me the benefit of the doubt that I meant it to be as as far from incendiary as possible.


The perhaps unfortunate metaphor the womb implies is that developing in MS tools is like being a defenseless premature baby who needs to be protected from the big bad world until they come to full term and leave the MS environment behind.


To me, this metaphor could be extended to [whatever environment you first really liked]


I am delighted to give you the benefit of the doubt. Thanks for the explanation.


We do and that was my original impression. Yet you listed a number of items even though they all boiled down to the same thing as far as I could tell. Nothing wrong with having different tastes, I just wanted to see if I was missing something deeper in each point.


I stopped at the part where you compared ReSharper to VIM

I'm guessing you like to debug using print and the command prompt as well?

For most the world keeps on spinning, for some it stops in their youth. Sad.


Hell yeah vim rocks!

So too does the command line! high five


> 1. "Strong and intelligent"? "Convenient"?

Not a C# user, but few languages of the strength and intelligence of types in ML. Covariant arrays (which C# presumably adopted for the sake of Java programmers) moves an easy, and obvious, compile time type check to runtime. The type inferencing in C# is very weak relative to ML/Haskell. The type syntax is generally more lighweight in ML/Haskell as well. For example a function that takes a list of some type ('a) and returns the head of the list if it exists or None if it doesn't looks like:

val hd : 'a list -> 'a option

Of course, all if this works in unison with other type functionality that is far more lightweight in ML than C#. The function above is only really so succinct because I have powerful variant types and pattern matching on them. I think they go hand in hand because using an option-type is somewhat useless if I don't have something like pattern matching to access its state.


C# includes the static LINQ method "FirstOrDefault()" for any types implementing IEnumerable, e.g.:

    var firstNumber = numbers.FirstOrDefault();
(This isn't strictly the same as what you described if "None" is different from "null".) Once such a method exists, does it matter how many lines of code it is written in? I posit that every language feature in a reasonably intelligently designed language serves a valuable purpose, and that everybody has different needs, and the verbosity in C# serves certain needs.

Here's an implementation in C# that is a function on types T implementing the IList interface (rather than IEnumerable like LINQ does):

    public static T Head<T>(this IList<T> list) {
      return (list == null || list.Count == 0) ? null : list[0];
    }
It is used in the same way as the FirstOrDefault() example above. Much more verbose, I'll agree! But the real question for this thread is is it less convenient, and if so why? All the verbosity stems from just a few logical places:

1. the 'option' function you have in your example. In reality, your 'option' is doing all the work I did above, which means that comparing the verbosity of your sample code to the C# equivalent of FirstOrDefault() is perfectly valid in my opinion :)

2. the explicit return type. This serves a valuable purpose in my opinion.

With your code, it's easy to break the hd function by changing the implementation to return a different type. You might not even notice the bug if the different type's implementation is close enough to the right one. I once had a bug like this in VB6 years ago: I changed a method to use an integer instead of a string, and VB's automatic type casting happily allowed the rest of my code to function... until in the middle of a demo I ran a function I hadn't tested and default value of 0 broke something expecting an empty string! My lesson: type inference can be dangerous.

It also makes refactoring a large project much more difficult in my opinion because there is no way to distinguish between specification and implementation. Consequently, changing your implementation runs the risk of breaking your specification without being informed.

There is a school of thought that says your unit tests should cover this aspect of the specification... but then all I'm doing is implementing explicit types in a roundabout way - let the compiler do that I say! That being said I don't know if ML/Haskell have 'interface specification' types to ward against this, where you need it (and everywhere else you get to save on verbosity)? That would be a nice improvement to C#: private methods can use extended type inference, anything public (including implementing an interface) need to be more explicit.

3. syntax: visibility (public), static modifier and return statement. This is a matter of personal preference. With Resharper in Visual Studio each of these words costs me 2 or 3 keystrokes, and if I like I can use templated code snippets to reduce that for any situation, so I'm not too bothered :)


I think you're underestimating how much work the type system is doing here: `option` isn't a function. It's part of the return type definition. The whole point of having a type system as strict as ML's or Haskell's is to avoid the sort of problem you had in VB6. Automatic typecasting is a very, very different beast to type inference: in Haskell, your problematic code likely wouldn't have got past the compiler. You're precisely wrong when you say that it's easy to break the hd function by changing the implementation to return a different type. If you do that, your program won't build.


ML's and C# have their strengths and weaknesses, but from a type theory perspective, ML's type system is simply much more powerful when it comes to ensuring correct code than C#'s. Take, for example, your implementation of Head, what if you messed up and wrote:

> return (list.Count == 0) ? null : list[0];

Would the C# compiler catch it or would you not find out until runtime? An ML compiler would tell you that function is wrong.

What about using Head? If you have a list of integers, will the compiler let you do:

> Head(integer_list) + 2

An ML compiler won't, because it's not safe.

> This isn't strictly the same as what you described if "None" is different from "null".

It is, but the important thing to note is I had to specify that the function, 'hd', can return 'null', by wrapping it in the option type. 'null' is only a valid value for option types. If I write:

> val hd : 'a list -> 'a

I could not return 'null'/None, it simply isn't valid. And the compiler wouldn't let me.

> the 'option' function you have in your example

'option' is a type, not a function (I suppose you can make some high level argument that all types are functions over values or something, but not relevant here). And 'option' itself isn't really even a type, it requires a type variable, so "'a option" is a type where "'a" is replaced by a concrete type at some point. It's not doing the work you described above, it's actually defining a contract between me and users of hd and the compiler.

> With your code, it's easy to break the hd function by changing the implementation to return a different type. You might not even notice the bug if the different type's implementation is close enough to the right one.

No, it isn't actually. If I change the return type the compiler will not compile my code.

> I changed a method to use an integer instead of a string

This cannot happen in ML, the compiler won't compile it because an integer isn't a string. What you described is not type inference, it's dynamic typing. The only danger of type inferencing in ML is annoying type errors during compilation. I'm not sure you actually grok what type inferencing is.

> It also makes refactoring a large project much more difficult in my opinion because there is no way to distinguish between specification and implementation. Consequently, changing your implementation runs the risk of breaking your specification without being informed.

Actually, refactoring code in ML tends to be pretty easy. If you change the type of something the compiler won't compile your code, so you know instantly where you messed up and can fix it. You can construct code to make this not work, but in general that isn't the case. See https://ocaml.janestreet.com/?q=node/101

> let the compiler do that I say!

Exactly, and an ML compiler definitely does more of this than the C# compiler. At a cost, of course.

Your post has several statements which suggest you are ignorant of ML and type theory. If you're interested in learning more, check out Benjamin Pierce's book "Types and Programming Languages". Or just spend a weekend with Ocaml or Haskell and prepare for the compiler to frustrate you with it's strictness :)


As far as #4 is concerned, windows is not configurable and the "command line" is an absolute joke, which renders the idea that it has a better developer experience absolute rubbish :) The command line is super powerful and highly convenient in a developers hands.


The lack of configurability is a bug or a feature, depending on your purpose and your disposition. I personally can't stand configuration nightmares. I want things to just work as much as possible, and in my experience linux has a real problem with that. I'm a programmer, not a configuration engineer. I expect the systems I use to understand that.


PowerShell is no joke. Even if you never intend to use Windows again, it's worth taking a look at, there are some interesting ideas in there.

(Overall though, I agree that Linux and Mac give a better developer experience.)


Seeing as how I've been developing for the last 15 years on Windows and haven't found the lack of a *nixy command line to be an issue I'd love to know what you're doing that I'm not :)


Well, the command line basically means you have the power of a full programmming language at your fingertips for administrating your system, your projects, your build system, etc.

Also, the homogeneity of the unix interface (pipes and utilities that uses them) means that you also have very powerful primitives to work with.

Proving the utility of the command line could benefit from a few full featured examples, but this would need a longer post that i'm in the mood of writing right now :)

With that said, you have a powerful administration programming language on windows called powershell. It's much better than bash by a lot of metrics. The way it's integrated into the system is not very good though. For example, the terminal client sucks, and the security system is way too complicated for casual use.


I Spend a lot time in the command line on Windows. Powershell, GnuUtils, and the Windows server resource kit make it much less of a joke than it used to be.


The only way to know if there is a world out there that you're missing is to go explore it. The biggest problem with the .NET world is that the vast majority of .NET developers have spent the vast majority of their time in it.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: