Hacker News new | past | comments | ask | show | jobs | submit login
I Lost Faith in the Industry, Burned Out, but the Cult of the Tool Saved Me (habr.com)
169 points by curtis 60 days ago | hide | past | web | favorite | 72 comments

I think F# just fits the way he thinks really well. I have that same sort of Zen experience when I code in Ruby. Ruby's object model sets like firmament in my mind and allows me to think about blobs of Ruby code in a discrete rather than a continuous fashion. Which is what I think the author is getting at with his praise. Abstraction verbosity golfed down to a minimum.

I couldn't even imagine giving up Ruby's elegant object model for the verbosity of a static typing system. So when people tell me that Ruby or Rails is magical, I'm like, yeah, in a good way. But the arcane correspondences just aren't very accessible. If I want a functional programming model, I just code one up out of the basic building blocks of ruby. If I want a type system, I can make one.

Don’t languages like F# remove much of the verbosity of a type system? In fact, we’re finally loving away from that verbosity in general. Swift, for example, lets me write declarations like this:

var done = false

let language = “russian”

let languages = [“english”, “french”, “german”, “russian”]

let dictionary = [1:"english", 2:"french", 3:"german", 4:"russian"]

The types are implied. The use of ‘let’ means immutable.

I believe F# goes even farther but the verbose typing of a generation ago is almost gone.

This. Static typing doesn't mean tedium in modern languages. In Java and old C#, you would write stuff like this:

  int x = 1;
  BigOldClass y = new BigOldClass()
  Dictionary<int, List<string>> z = new Dictionary<int, List<string>>();
  int[] p = new int[] {1, 2, 3};
In modern C#, you can write cleaner code like this:

  var x = 1;                  // type is inferred
  var y = new BigOldClass();  // syntactic sugar to avoid repetition
  var z = new Dictionary<int, List<string>>();
  var p = new [] {1, 2, 3};
Still strongly, statically typed, and obviously typed.

right, most languages do this now. But F# extends that further. Function signatures don't need types either. Record types can be inferred from their field names. If you write a function with no type signatures it is automatically made generic, as well.

So you can often write large chunks of code with no specified types, but everything is still statically typed. Granted this is maybe not always good, sometimes it is productive to have types in the function signatures, though the IDE can supply these automatically with codelens featuers/on hover features.

To me that isn't an improvement. It may be "statically typed", but it isn't clearly typed, the first set is not slow to enter or read and is 100x clearer.

To me var is only useful for writing generic lambdas & similar - you shouldn't use for regular variables.

I agree that "1" should annotated to specify which type of integer you are using. But IMO `var foo = new Foo()` is pretty clearly of type Foo. Furthermore the type is often not what I care about (the variable name and function call names generally tell me more about what is happening on that line of code), so I'm generally pretty happy if the type is a little more hidden.

The problem likely results from:

var i = someFunctionWithANonObviousReturnType();

Type inference may be easy when the left-hand side is a literal, but that's often not the case. And when the left-hand side isn't a literal, it definitely optimizes writing code at the expense of reading code. You can argue that IDEs should be helping us by show us the type when we mouse over or auto-completing available methods on the variable's type, but there are situations (code review, pair programming, etc) where we can't rely on tooling to help in this regard. I get why people like type inference and I use languages that have it on a daily basis, but I've also realized that I need to adapt my coding style to it or I'll end up with code that's difficult to read. In particular, I'm constantly considering whether the code I'm writing is understandable using local reasoning only and that I'm not forcing a reader of my code (who's likely to be me several months from now) to jump to some declaration somewhere else in the code.

Then add a type in that instance.

var i:ReturnType = someFunctionWithANonObviousReturnType

I found that in practice I don't need it.

This is also true in Java now

C++ also has auto now.

Function types don't need signatures in Java?

I, as a F#'er, would argue that lines 2 and 3 of the C# snippet there is not an example of type inference. But I realise this is not a mainstream opinion so please don't downvote me :)

No, I don't think I meant to say all the lines were type-inferred. The type inference only happens on the 1st and 4th lines. In 2 and 3 the types were explicit.

All lines however are statically typed.

The type is inferred on all 4 lines and the mechanism is little different; the 4th line is the most complex one, because there's a type unification required across all elements of the array, but C# and Java and other languages with overloading already need to do very similar inference with the ternary operator - the types on each side of the branch need to be unified to resolve the overload.

Hindley-Milner type inference (like used in F#) isn't hugely more complex; it's more aggressive at using type variables to generalize the code, and applies type unification in more situations.

> verbose typing of a generation ago is almost gone

Worth pointing out such type inference is hardly a new idea e.g. see the ML type system: https://en.wikipedia.org/wiki/ML_(programming_language) which Wikipedia says first appeared in 1973. This actually traces its roots to the typed lambda calculus which predates electronic computers.

However I think it is fair to say that type inference systems are finally appearing in more 'mainstream' statically typed languages (for example the auto keyword in C++ and your Swift examples).

Yes, immutability, pattern matching, type inference, etc are not new ideas.

Pattern matching is another concept that’s making its way into popular programming.



C# and Java are also adding this feature.

We’re taking the long way to becoming Haskell and oCaml programmers.

F# comes from ML by way of OCaml.

yes, type inference is pretty comprehensive, function signatures don't usually need them even.

It's probably a little multifaceted. I'm a fan of Ruby but even Ruby develops warts with a code base that's too large.

I was just about ready to exit programming entirely before I discovered Elixir, just because I got tired of dealing with the...exact...same...problems...in...every...single...dang...language.

Elixir essentially forces you into a mode of operation that hits that perfect balance of productivity and concise code with a structure that inherently avoids almost every long term code base problem that I've dealt with in my career.

It got me excited about programming again, which I didn't think was possible that far into my career.

One of the things that made the article so interesting was that the author would try to find salvation in a solo side project, only to find himself right back in the same hell.

I've been through this same problem, even with Ruby. What I ultimately had to realize is that you have to just get really really familiar with Ruby before you can get a taste for its superpowers. I don't think it has all that much to do with languages / stacks, though I do think it matters to a certain extent.

I personally don't mind seeing warts in a Ruby codebase, because it means I'm at least working with Ruby. I've been violently dragged kicking and screaming away from Ruby more times than I ever want to see in my career.

I'm pretty comfortable with Ruby and have been programming in it for over 5 years now. But for any new project from now on I'd try to use a Typed Functional language - Reason/OCaml, Haskell etc.

I used to argue strongly against types and functional programming, but that was before I actually knew the paradigm at all. I wonder if you have tried building anything non-trivial in a Typed Functional language, or even a different paradigm like logic programming. If you have and are not convinced, that'd be a great discussion to have. If you haven't, then based on my own experience, I think you might shift your opinions after spending a year or so with it.

Erlang did that for me. I've looked at Elixir, but never used it in a project.

I have the same feeling when writing Go. I highly value minimalism and simplicity and I like understanding how things work on a deep level by reading and reflecting on it before actually using them. That's what Go lets me do. If I find a function, I know I can just llook at what that function does by looking at its source and I will probably figure it out. I don't always do, of course, but knowing I can gives me comfort. For my mental model, a function main with a lot of boilerplate code that sets up the database, the loggers, the services, injects all the dependencies etc. is much clearer than a directory of models, a directory of controllers, a directory of god knows what and some divine code sitting somewhere deep in the guts of rails doing something utterly magical and incomprehensible to make them work. I think a paradigm like Ruby's, where each line represents some clear and simple to understand fact about the code, in simple terms, like has_many :comments, would be much easier to understand with better tooling. I imagine a programming language (along with a very good IDE) where you could just hover over 10.days.ago and see exactly where that monkeypatch was defined and what it does.

I get the same feeling when programming in Ruby and it seems like the entire community appreciates the same ascetic. I just love working with Rails.

Matz took a unique approach when he decided to optimize for programmer happiness vs the machine and it turned out awesome.

Ruby is a beautiful language but to me it’s too hard to understand what ruby code is doing by just looking at the code because so much happens with monkey patching and such.

I used to feel exactly the same way about Ruby. Since then I have grown to value strict compile-time checks more so I don't really use it, but I still miss it.

I wonder though if you have evaluated Crystal?

Great article!

I had a similar revelation with Lisp and Clojure. Writing in a language you enjoy using and are very productive in (effort->effect) is really liberating.

The great thing is even when I have to return to the old ways (Java) Clojure's java interop makes writing the Java a lot more manageable, almost fun.

I don't know whether it's the functional paradigm, the dynamic typing (I didn't have the same revelation with Ruby/JS), immutable collection by default, the minimal lisp syntax, etc.

It just feels right.

And of course surveys of recent years shown that the highest paid PLs are F# and Clojure. Coincidence? I don't think so.

>> ... programming in TypeScript every day.

That explains why OP got depressed, considered quitting the industry and then switched to F#. If I keep using TypeScript, I might just snap and switch to COBOL.

Why the hatred of TypeScript? The vast majority of TypeScript devs tend to love the language in my experience

Every single person I've met who "hates" typescript really "likes" javascript and was one day forced to start working on switching over to static typing.

Maybe not this person's case, I wouldn't be too surprised if someone has another reason, but I seriously haven't heard anyone else with a strong opinion against typescript founded upon any other context. I could probably google around for other reasons.

I can totally see someone used to soft mutable js objects going insane in ml land.

>The vast majority of TypeScript devs tend to love the language

That sounds kind of like saying, "the vast majority of people with cilantro in their burritos like cilantro."

Due to Javascript Stockholm Syndrome it's more like "the vast majority of people forced to eat burritos at their day job like cilantro on their burritos."

it does, kind of, because sometimes you need to fit yourself to the project / company that already had some codebase before you. So I sometimes interact with languages that I like a bit less whilst working.

But in general, yeah, it's kind of like that saying :)

I suspect that developers tend to fall into roughly two kinds of backgrounds. That is static/compiled (c/java/etc) languages vs dynamic/interpreted (ruby/python/js/etc) languages.

Perhaps this leads to a different coding styles & expectations. The former end up with a preference for strong typing. Perhaps their coding style also depends on that.

The latter don't seem to find types to be a source of bugs.

To me it seems more like two wildly different forms of PTSD. For the former group (those who prefer static typing), unmaintainable and poorly documented spaghetti code has been the most identifiable source of pain in their career while for the latter, it has been long iteration times due to bureaucratic nightmares caused by compiler and management.

Like early childhood experiences, each programmer I've discussed the subject with seems to have their own unique professional experience that has molded their opinions. It's less a religion and more a coping mechanism for bad experiences.

For me, bureaucracy and TypeScript go hand in hand. There are benefits to using TypeScript, but they're not worth the problems.

TypeScript gives an illusion of added productivity and safety but when you actually look at results, bug density is exactly the same as JavaScript.

As a TS developer, most of your time is spent on catering to the compiler's complaints, but doing so doesn't actually yield any benefits in terms of producing high quality software.

TS and TSLint are bandaid solutions which don't address the root problems behind what makes code bad (which 99% of the time comes down to bad design/architecture). TS simply cannot prevent bad design/architecture - It can only formalize it.

It's nice to have linted code with everything typed and statically locked into place but that means very little in terms of actual code quality.

The reason Typescript has been so successful is that many of us don't have a choice in whether the beauracracy exists or not and have to pick up the pieces. Typescript is, by definition, a bandaid. That's what it was designed for.

Personally I've had a good amount of both experiences, and that's convinced me that either one will suck if you let them. Only good development practices can save you.

This is a very old conversation; it's worth noting that dynamic compiled languages (e.g. common lisp) are a thing also. Having a good REPL can change the way you look at it also.

Weak typing and dynamism just don't go well together, but other combinations are fine. Strong dynamic typing can lead to an "exploratory" approach to coding that looks a bit like what the author describes except that you can do it in such a way that your program compiles and runs the whole time you are fleshing it out, which is nice.

I find this sort of exploratory programming to be by far the most productive available in a research/prototyping mode. I do think the model falls down a bit when multiple programmers are involved, and in production. You could argue this is a feature though, as it is strong incentive to not ship the prototype ...

I suspect it was more a hatred of the web stack in general.

Hah! This fits with the point of the article I guess.

I mean, I started with Pascal, then C and C++, then PHP, then Ruby then C# and some Java, then Scala, then JavaScript, and now Elixir and TypeScript. Of all those, I like TypeScript the best (barring the NPM / deployment / transpilation / packaging mess, that is). I mean, I like it the best by far. It combines functional with OO much like Scala set out to do, but in a much simpler and more accessible way. I really love it, I think in its structures. All JS I write feels like TypeScript. Even a lot of the Elixir I write (for better or worse) feels like TypeScript with a funny syntax.

I now feel like structural typing is the big missed opportunity of most statically typed languages. The fact that I can define a type simply by writing, say, an object literal, continues to make my day.

    let a = { name: "Pete", age: 6 };
    a.nmae = 7;                   // type error!
    a = { nmae: "Mike", age: 7 }; // type error!
or even

    function moo() {
        return { name: "Pete", age: 6 };

    const a = moo();
      |name       string    |
      |age        number    |
Stuff like this feels so normal so quickly that I feel like I'm using yesterday's tools whenever I either don't get the autocomplete/type error (hi Elixir, Ruby, Python, PHP), or I have to manually add a `type Moo = yada yada` declaration above my function (hi Haskell, Scala, C#, F#, Java, srsly all of them).

I'm not claiming any of this is new. I mean truth is I haven't seen any Haskell or F# or Scala code that does this, but I bet that's just my limited exposure to functional languages. I've heard these languages are pretty powerful, so sorry if I insulted your cult there :-)

Still, I've used no other language that enables this ability to write code the way one would write dynamically typed code - head-first, straight down into the operational stuff, and finetune the data structures as we go along - while still getting type checking and all the confidence and tool support that comes with it for free.

I agree with the OP. TypeScript is my cult. <3

Next version of F# is bringing anonymous records, which can give similar experience to your structural typing examples.

In a way, I think the problem is that TypeScript doesn't go far enough: it adds static typing, but has to awkwardly attach those types to APIs intended for a weakly-typed language.

Yes, JSON is by far the most popular data interchange format ever invented. This is for a good reason; it's the most compatible format possible; it works with any language and any system regardless of what kind of typing is used.

As compatible as it is, there is definitely a lot of friction/awkwardness around type-casting JSON objects to typed objects within a statically typed programming language; there is an impedance mismatch. It's not as awkward as trying to type cast objects designed under one type system to a different type system (e.g. SOAP APIs) but it's still really awkward.

The impedance mismatch goes away completely if you use a dynamically typed language like JavaScript. For example, JSON produced by a python program can be parsed by a JavaScript/Node.js program without any issues.

The root reason why types are bad is because they're opinionated. There is no absolute right way to type something; and because of this, it's not correct to force other projects which interface with your project to adhere to your type definitions.

F# was one of the most frustrating languages I ever tried to learn. It sits in this uncanny valley between OCaml and C#. I've had many, many problems where I spent tons of time searching for idiomatic way of doing something only to find out that there is none and I have to use "normal" .NET classes. The documentation is pretty bad.

I find myself highly productive writing "mostly functional" code in C# using LINQPad. The only thing that drags me down is that it doesn't have good support for data structures that you can copy on the cheap a-la Clojure. The paradigm of "copy the world on change" is immensely powerful. Maybe I should look deeper into this. It seems that basic data structures for this wouldn't be very hard to implement on my own.

I think the ideal language for me would have functional, strongly typed capabilities for low-level operations and dynamic, late bound objects a-la Smalltalk for high-level stuff. But for this to work well it would need to have strong mechanism for versioning references, so functional parts don't get contaminated with mutability.

Oh, another thing about C#. I really wish it had an ability to concisely say "take an object, create a subclass of its class with additional properties, instantiate it, copy all values from source and then set extra properties to what I just provided".

  Something x = new Something { Property = "Hello" };
  var y = x.Expand(me => new { AnotherProperty = me.Property + " World" });
  Assert(y is Something);
  Assert(y.Property == "Hello");
  Assert(y.AnotherProperty == "Hello World");
Seems like a weird piece of functionality, sort-of-union-types, but it would save me tons of code in many, many contexts.

I can implement this using reflection, but it will be slow and complicated and not worth the effort. Maybe the new tuple mechanisms can do something of this sort with minimal overhead and syntax. Need to look into them.

Things never got so bad for me that I «lost faith in the industry», but I definitely had the same reaction to F# as you did.

It just makes programming so much more fun — all the tedium is gone, it feels like every character you write is actually important.

OP will ascend to the next level as a programmer when he realizes he doesn't have to do all that enterprise crap in C# either.

My bullshit detector was wailing at full blast reading this. The details seem extremely exaggerated. I guess you could call it storytelling.

Maybe I'm just annoyed by his slight on public transportation.

He has a .ru email, so I'm guessing he's from Russia, I don't know where from exactly. I don't know anything about public transportation in whatever part of Russia he lives, so why would I judge him about that?

Good point! I probably rushed to judgement. A little more believable then, that his car spontaneously caught fire and he walked home 10km -30C in inadequate clothing when he had another option available. Also the corporate corruption, the fake broken English, etc. makes somewhat more sense.

Thank you for this article. The point is not F#. The point is finding a philosophical fit with your code .. a kind of "Drift mode" (like the Del Toro movies - https://m.huffpost.com/us/entry/us_3696719)

For me, it was Ruby. I have stopped writing Ruby code and have found other languages that I love now (yes including Typescript), but nothing will replace the enlightenment rush of Ruby.

I think all programming languages that exist today are a symbolic representation of different kind of mental models that humans have. When you find one that synchronises with your mental model, it's just magical.

I'm getting burned out on all the narcissists writing articles about how they thought everyone else was an imbecile, and rudely raged at everyone most of their career, and how great they are because they got slightly less cancerous. Life is too short for that, if you love something, or have a technical opinion, be a kind hearted teacher and discusser not a rage monster.

This comment probably breaks the site guideline which asks: "Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize. Assume good faith."


I didn't read it super closely but it seems obvious he's just using that language as a form of self-criticism. No need to pile on. (Edit: also, I think some of this is cultural differences.)

Did you ever read Flatland? Spoiler alert...

The thing about Flatland is the twist at the end, when Square asks Sphere about even higher dimensions (than three) and Sphere is unwilling to consider them.

(I guess what I'm saying is, if you like Functional Programming, try Prolog. Logical Paradigm is even more useful than Functional Paradigm.)

I've been meaning to look into F# and this post gave me the motivation to do so again.

However, trying to get up and running on a Mac, the documentation is horrible. I looked at several of the official guides, including the Microsoft site on how to get running with VSCode, and none of them worked without having to search for the error messages.

Now that I'm up and running, none of the docs I could find would tell me how to build/run a project.

It appears you to install the .NET Core SDK, and now trying to build a project gives some ".NETFramework,Version=v4.6.1" error.

What a nightmare. I almost expected this since this is a .NET/Microsoft related project.

Right now, there is a transition between net framework and net core in the MS .net world, which causes plenty of confusion to newcomers. Which is sad, because the clean, .net core way is the simplest thing in the world:

1. Install the dotnet core sdk 2. From a command line in an empty folder, run `dotnet new console -lang F#` 3. To build or run use `dotnet build` or `dotnet run`

Wow, that actually worked. Wish those were in the official docs so I wouldn't have wasted an hour trying to debug...

I followed the VSCode instructions and using "F# new project" apparently gave me a .fsproj with the wrong TargetFramework, and after changing that, then got a "Could not load file or assembly 'FSharp.Core" error.

Yeah, sorry. dotnet core to me has been an attempt by MS to not just get cross platform, but also to rewrite everything to get rid of the old kludges. Simple CLI, simple fsproj files, easy nuget, no differences on linux/osx/windows.

But there is still a lot of outdated docs out there. dotnet core has just come too fast, imo (though I'm thankful). These MS docs are accurate: https://docs.microsoft.com/en-us/dotnet/fsharp/get-started/g.... Though it does specify the use of sln, which isnt necessary for small projects.

Try here https://dotnet.microsoft.com/download. We also put some beginner videos on our you tube channel here https://m.youtube.com/compositionalit.

I have been making a transition to be a functional programmer for a few months now because similar concerns and stresses. I know that the programmer of the future will construct programs like mathematicians do. The madness of the old ways brings too much grief.

This headline sounds like it's from an HN headline generator

This is a scarily similar story of how I ended up in F#. I'd reached the peak of C#. Every possible best pattern and practice (including the ones that were still a few years away) I'd already done them and perfected them. I was so high up on the peak of the C# mountain that recruiters could no longer understand me, employers could no longer understand me, co-workers could no longer understand me. I could no longer function on projects and teams this way. I ended up doing almost nothing except "R&D" for the teams I worked. None of my R&D ended up in the product. I wrote specs, whitepapers, all sorts, something to try to contribute. Nothing seemed worth the hassle. Toward the end probably a couple months had passed where I'd made no commits, and had barely typed a single letter into Visual Studio. I became an imposter and a pretender - my self confidence took a huge knock. I quit and went back to what I knew best. Building my own businesses with a trusted old business partner. Some of the R&D I did in that last team was in F# and I chose to use F# in my new venture. I directly attribute F# as having had literally pulled me out and saved me from that year of burnout. And made me completely unlearn everything that was wrong about software development and relearn it the right way. The transformation took about 4 months and I'm now 5 years since that time and I never look back. The thought of going back to C# and all that crap ... I think I would rather earn money smuggling drugs or something.

I think the lesson is: If you get burnt out, try changing your tooling for something more modern. If it ever happens to me again - I will try to have the clarity of mind to do it again.

> I'd already done them and perfected them. I was so high up on the peak of the C# mountain that recruiters could no longer understand me, employers could no longer understand me, co-workers could no longer understand me. I could no longer function on projects and teams this way.

Object-oriented programming has its drawbacks, of course.

But your monologue here is a bit grandiose. It reminds me of the narrator in Zen and the Art of Motorcycle Maintenance as he reached the pinnacle of his insight into metaphysical Quality. He was actually just going mad, of course.

Maybe you had communication difficulties with other people because you were having a severe case of burnout that verged on a nervous breakdown? In any case, I'm glad you're feeling better.

Slightly off-topic: Did you read Pirsig's sequel, Lila? He goes into the structure, levels, types of Quality in great detail, and it makes a lot of sense. There's a lot about anthropology, and about society vs individual, static vs dynamic etc. It's pretty great too, I've read it a few times, doesn't have that over-heated feel of ZAMM.

"Object-oriented programming has its drawbacks, of course."

They say it takes 10 years to become an expert in anything. In my case, with OOP, I did it in 6. I would highly advise to anyone that once you become expert level in anything - don't hang around for long: make sure you've got the next meal ready for your brain as it will hate you if you haven't.

Was not intentionally grandiose. The intention was to avoid superfluous detail given the context.


> They say it takes 10 years to become an expert in anything. In my case, with OOP, I did it in 6.

I'll take it on good faith that you are indeed an expert, and are a gifted learner. Great! I'm jealous :)

But you might want to consider the way you talk about yourself to other people. Even from a selfish perspective, becoming an expert in communication reaps many rewards - and the right level of humility is very important in communication. Both your comments come off as very egotistical, although I'm sure that wasn't your intention.

Hi yatherm. Thanks for the comments. It is possible, after reading the OP article and being caught up in the shock moment of "this guy's story is identical to mine!" that I commented in a bit too much of a "Alcoholic's Anonymous" style that is inappropriate for HN. Ah well.

I mastered it in just one day after realizing object oriented programs are just objects in the category of medial magmas.

> Every possible best pattern and practice (including the ones that were still a few years away) I'd already done them and perfected them.

Care to provide examples of the patterns? Genuinely curious

> I was so high up on the peak of the C# mountain that recruiters could no longer understand me, employers could no longer understand me, co-workers could no longer understand me

What sort of things were you saying that led to people not understanding you? I can't imagine knowing a language so well that others can no longer understand me

Not the OP but I can maybe guess at some of the examples. In Java (and I assume C#) because you don't have a powerful enough object system, you end up having to write contorted code often making extensive use of various "design patterns" in order to support testability, the SOLID principles, not having to rewrite a bunch of existing code, and so on. So much of that melts away when you instead write in a FP style, or use a more powerful OO system (like CLOS) or use a dynamic language. I've worked with people who couldn't describe the most commonly used patterns in Java (Visitor for instance) or techniques (e.g. "Dependency Injection") but also people who seem to think everything should be expressed with them, including those that are obsoleted by modern Java features.

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