Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

F# always struck me as one of the most terribly underrated languages. I'm a lover of MLs in general, but F# lands on one of the sweet spots in PL space with ample expressive power without being prone to floating off into abstraction orbit ("pragmatic functional" is the term I believe). It is basically feature complete to boot.


I agree on the underrated part.

My theory as an outsider: F# is strongly tied to the Windows world, the corporate world, where a conservative approach is always preferable, on your tech stack and if you need to hire peons coding all day. The corporate world isn't leaving OOP anytime soon, because it's what 95% of engineers focus on, the silent majority which do not frequent HN or play with functional languages in their weekends. The corporate world runs on Java and C#.

If F# had been released in the open-source, flashy and bustling world of Linux and macOS developers, it would have had a much greater success.

I know you can run F# on Linux, but just like running Swift, it feels like outsiders I wouldn't want to bet my business on if I were a Linux-only shop (which I am), however nice it feels. Also a decade ago when it had a chance to take root, Microsoft was still the Embrace Extend Extinguish company. It's not good enough to risk it, just like I'm not gonna use SQL Server for anything.


I am admittedly biased, because although I started programming recreationally in the LAMP-stack world of mid-aughts fame, a huge portion of my professional career has been in C# and the .NET stack.

I think you are grossly overestimating the degree to which the programming language you choose to use to solve a business problem constitutes "betting your business on." How would your business fundamentally change if your first 10k lines of code was in F# as opposed to Go, or Java, or Python, or TypeScript? These are also all languages I've been paid to use, and have used in anger, and with the exception of Java were all learned on the job. This comment in general has big "M$ bad" vibes and if you take those pieces out I'm not sure what the actual criticism is (maybe there is none)?


Aside from the EEE quip, I didn't catch any "M$ bad" vide in GP's post.

I think the situation is clear-cut: until recently, you couldn't really run .net on anything else than Windows, so the only people using it were those already invested in the ecosystem.

Among the people invested in the windows ecosystem, many (most ?) are large "non-tech" companies who hire people who mostly see their jobs as a meal ticket. These people don't have the inclination (for lack of curiosity, or time, or whatever reason, doesn't matter) to look into "interesting" things. They mostly handle whatever tickets they have and call it a day. Fiddling with some language that has a different paradigm wouldn't be seen as a good use of their time on the clock by corporate, or during their time off work by themselves, since they'd rather spend that time some other way.

Hence, F# never really got any traction.


That's for coming in my defense. You are right. I'm not a big fan of Microsoft, but I also don't hate them.

It's pretty simple, really. I am a Linux engineer, and it is not a great investment of time and money for me to get into .NET. I knew F# was cool, but is it cool enough to want to feel a second class citizen, running it on the OS and platform it is not intended to run on? It makes no business sense at all.


> is it cool enough to want to feel a second class citizen, running it on the OS and platform it is not intended to run on?

I'm not a software engineer myself, nor a Windows person, so I don't know the specifics, but FWIW, my client runs production .Net code of the C# variety on Linux, connected to pgsql. It's some kind of web service for checking people's tickets (think airport gates where you scan your ticket to enter), so not exactly a toy, even though it's nowhere near unicorn-scale. It seems to work fine, in the sense that I've never heard anyone complain about anything related to this setup. No "works for me but is borken in prod" or "thingy is wonky on Linux but OK on Windows so have to build expensive workaround".

The devs run VisualStudio (non Code) on their Windows laptops. Code is then built and packaged by Azure Pipelines in a container and sent to AWS to be run on ECS.


I do get it. .NET now works pretty well on Linux.

But it never was a tier 1 platform during its growth. So most non-Windows devs put their focus on other platforms. There is nothing wrong with that.

I could learn .NET now, but I don't really have an interest to do so at this point; Also, the devs you talk about are on Windows, using their tier 1 IDE (Visual Studio) that only runs on Windows, which is my point exactly.


That's a fair point. Tooling is an important aspect of a language, at least for me. I don't know what the VS Code on Linux experience is like for .net.

I tried to dip my toes into F# out of curiosity, and it worked by following some tutorial and VS Code. But it did seem somewhat bare bones. Although I'll admit I'm spoiled by Rust and IntelliJ.


Working for an org who bet on a mix of scala, python, and typescript, I can tell you which languages are being bet on for the rewritten services, and which language is getting in the way of getting things done.


Am I guessing correctly that Scala is "getting in the way of getting things done"?


That's a Texas sized 10-4


What's so bad about Scala? I've only used it for hobby projects myself.


Using it in a context where you need to make money, it's a bad bet. Fine for academic ideas and such things, but really hard to build a business around. And the tooling, community, libs, and docs show how it just can't punch the same weight as other languages when at the end of the day you need to get shit done.


Care to elaborate? Are there any big frameworks in the mix that might have gone from oss to commercial?


We have both Akka and http4s in use, and are migrating to http4s for those services. We need to do more things more quickly with fewer hands. TS and Python are just easier and better tooled for the majority of our (CRUD) work.


scala is being rewritten in .. typescript?


dotnet compiles in general are slow AF on macs, and F# really stood out as the slowest last time I give it a kick.

F# looks wonderful, but unless you’re already in the MS ecosystem, dotnet just feels bad and out of place. And I guess if you are already in the MS ecosystem you’re using C#.


> This comment in general has big "M$ bad" vibes and if you take those pieces out I'm not sure what the actual criticism is (maybe there is none)?

As with almost all "vibes"-related comments, this doesn't hold up. There isn't any criticism; just a positing that the sort of corporate, process-heavy companies that will major on Microsoft programming languages will be the last ones to want to try functional programming languages.


Would agree with this. I don't think the language choice, is as massive bet on the business as people think. I've seen much more niche and ancient langs without an ecosystem (no libraries, no SDK's to popular products, etc) build very profitable products. I would see these languages as a much greater risk.

As long as it has a base capability (libraries, maturity) and when people join they can be productive with it in a month or so then the risk is pretty low. For F# most .NET developers, even Node developers IMO will get used to F# relatively quickly. From my anecdotal experience with a number of languages its probably one of the easiest to onboard out of the FP langs balancing the FP methodology while trying to be practical/pragmatic. It has a large ecosystem via the .NET platform and supplements it with FP specific F# libraries where pragmatic to do so.


When it's time to scale out your team and now you're trying to hire dozens of F# developers it starts to matter a lot more. You can throw a rock and hit a Java developer. I hate the language, but finding other people who can be productive in it is trivial compared to F#.


One of the common threads among companies I've worked at which I would consider "successful" is that they don't really classify developers based on what languages they've used before. If you're a good programmer you can become a net positive in almost any language in a few weeks to a few months, and productive within the first year. Some of the worst companies I've worked for were the type who would toss a resume in the trash because they had 1 year of experience in $LANG from a few years ago and not the "have used it for 3 of the last 3 years" they wanted.


I think it depends on what you mean by "successful". Surely multi-billion dollar financial organizations are by at least some definition successful. They are a complete shit show from a tech standpoint. They are so large they cannot effectively manage specialist developer staff outside of very narrow niches. Standardization when you've got thousands of developers across hundreds of products matters. Maybe some "successful" startup can make things work when they are small. But you'll find they start to standardize when they hit real scale.


.NET has great Linux support nowadays though. I only use Linux and use .NET extensively and have no complaints


Totally agree; F# really feels like a language designed by someone who really does understand the theory, why it's important, but also wanted to make the language realistic to use in industry.

When I was at Jet and Walmart, I never really felt "limited" by F#. The the language was extremely pleasant to work with, and I think most importantly, it was opinionated. Yeah, you can write Java/C#-style OOP in F# if you really want, but it's not really encouraged by the language; the language encourages a much more Haskell/OCaml-style approach to writing software.

Even calling C# libraries wasn't too bad. MS honestly did a good job with the built-in .NET libraries, and most of them work without many (or any) issues with the native F# types. Even third-party libraries would generally "just work" without headache. .NET has some great tools for thread-safe work, and I'm particularly partial to the Concurrent collections (e.g. ConcurrentDictionary and ConcurrentBag),

I also think that F# has some of the best syntax for dealing with streams (particularly with the open source AsyncSeq package); by abusing the monadic workflow ("do notation" style) syntax, you can write code that really punches above its weight in terms of things it can handle.

Now, on the JVM side you something like Scala. Scala is fine, and there are plenty of things to love about it, but one thing I do not love about it is that it's not opinionated. This leads to a lot of "basically just Java" code in Scala, and people don't really utilize the cool features it has to offer (of which there are many!). When I've had to work with Scala, I'm always that weirdo using all the cool functional programming stuff, and everyone else on my team just writes Java without semicolons.

But the basic point of the article does make a reasonable point; part of the reason that Scala has gotten more traction is because Java is just such a frustrating language to work with. Scala isn't perfect but being "better than Java" is a pretty low bar in comparison.

C# is honestly not too bad of a language; probably my favorite of the "OOP-first" languages out there. The generics make sense, the .NET library (as stated before) is very good, lambdas work as expected instead of some bizarre spoof'd interface, there are some decent threading utils built into the language, and it's reasonably fast. Do I like F# more? Yeah, I think that the OCaml/Haskell style of programming is honestly jsut a better model, but I can totally sympathize with a .NET shop not wanting to bite the bullet on it.


Martin Odersky is just a very nice guy and I get the impression that he isn't keen on saying "no", which is how you end up with a language that allows you to use xml tags inline (no longer supported in Scala 3),

https://github.com/scala/scala-xml/wiki/Getting-started

The "opinionated" Scala are the Typelevel and Zio stacks, which are very cool.

The problem with the "better Java" approach is that although it has helped Scala's growth a lot, it has also made it susceptible to Kotlin. The Scala code that doesn't use the advanced type magic can be straightforwardly rewritten in Kotlin instead. Kotlin also stops your bored developers from building neat type abstractions that no one else understands.

People who use Scala only has a "better Java" can now use Kotlin has a "better "better Java"".


Yeah, and I think that's why a language like Clojure, which is substantially more opinionated than Scala, has been relatively unphased by Kotlin. Clojure is much more niche than Scala, and the adoption has been much more of the "slow and steady" kind.

People who are writing Clojure likely aren't looking at Kotlin as an "alternative"; while they superficially occupy a similar space, I don't think Clojure has any ambitions of being a "better Java", but rather a "pretty decent lisp that runs on the JVM with some cool native data structures and good concurrency tools". I do like it better than Java, but that's because I like FP and Lisp a lot; if I needed a "better Java" right now, I would unsurprisingly probably reach for Kotlin.


Yep, Scala got a lot of attention because you could kinda write it like Java, and Java hadn't changed much in a very long time - people were looking for a "better Java" - and Clojure obviously isn't that.

Kotlin's whole point is a "better Java", so it's going to grab people who went to Scala for a "better Java". Also Java actually has a sane roadmap and methodology to get better too, so there's that now too - with the preview/incubating JEPs, people can see what is coming down the pipeline.


Yep, I don't dispute anything you said there, I think that's pretty consistent with what I said.

Clojure makes no claims of being "Java++". It's a lisp first and foremost that focuses on embracing the host platform and being broadly compatible with existing libraries and strong concurrency protections.


> without being prone to floating off into abstraction orbit

What do you mean by this?


"Oh, you _also_ need to print something? Lets stack a few monad transformers..."

"But remember that you need the TemplateExplicative and NullUnderstanding compiler extensions!"


You can use eventlog traces, from Debug.Trace [1]. You can (traceEvent $ "look: " ++show bazinga) everywhere you need and then stare at the log to your heart content.

[1] https://hackage.haskell.org/package/base-4.18.0.0/docs/Debug...

No need for extensions, just compile and run your program slightly differently. That's the power of declarative languages.


Not everything is tracing and debugging, sometimes you really need to output intermediate results for "normal", "production" purposes. One could still abuse Debug::Trace, but that would really be ugly.

I also object to that "everywhere". It is far easier to just dump an extra 'print' line somewhere inside a for-loop than into a `foldl (*) 1 $ map (+ 3) [17, 11, 19, 23]`. And that is an easy one...


With eventlog you have lightweight profiling and logging tool for "normal", "production" purposes. You can correlate different metrics of your program with your messages. This is not an abuse of Debug.Trace (notice the dot), it is normal state of affairs, regularly used and RTS is optimized for that use case.

I develop with Haskell professionally. That foldl example of yours is pretty rare and usually dealt with the QuickCheck [1], mother of all other quickchecks. Usually, the trace will be outside of the foldl application, but you can have it there in the foldl argument, of course.

[1] https://hackage.haskell.org/package/QuickCheck


Eventlog is just `unsafePerfemIO*`, so you could just use that instead and not "hide" it and feel better about that.

The "correct" answer to `foldl` would be `scanl` and printing the result of it.

https://hackage.haskell.org/package/base-4.18.0.0/docs/Prelu...


Eventlog traces are RTS calls wrapped into unsafePerformIO, you are right. The trace part of eventlog is optimized for, well, tracing and is very, very lightweight. It is also safe from races, whereas simple unsafePerformIO (putStrLn $ "did you meant that? " ++ show (a,b,c)) is not.

In my opinion, eventlog traces make much better logging than almost anything I've seen.

Right now, developing with C++, I miss the power of Haskell's RTS.


The point I was trying to make was, that if all you want/need is a `putStr`, just use `unsafePerformIO`.

Haskell's (GHC's) Eventlog is nice, but a binary format and not comparable to text output.


> I develop with Haskell professionally. That foldl example of yours is pretty rare and usually dealt with the QuickCheck [1], mother of all other quickchecks. Usually, the trace will be outside of the foldl application, but you can have it there in the foldl argument, of course.

So actually not everywhere. And QuickCheck does something else entirely.


You missed the word "usually". You really, really do not need a print within the body of a loop of any tightness. But you can have it.

The foldl example of yours should be split into a property checking and controlling for expected properties of the input. The first part is done via quickcheck and second part usually is done with assertions and/or traces.

But nothing preclude you from having your trace there, inside foldl argument. This is clearly wrong place to have it, but still you can have it there.

So I repeat, you can have your traceEvents everywhere.


I can't tell if you are trying to defend those languages or just piling up absurdities on the previous post in the style of "yes, and ..." improv.


I am trying to offer counterpoint to what seems to me as an unjust critique from a person who, at first sight, does not know much about Haskell.

Also, a link to a useful library is not a bad thing for anyone curious about Haskell. Thus, there's a bit of education there.

If it looks like improv, I am here every evening till Friday.


You're thinking of Haskell. F# was modelled after OCaml, which doesn't attract monad transformer stacks, and doesn't have a zoo of compiler extensions.


Well, they aren't actually compiler extensions but pre processor extensions (PPX).

And I would really like if OCaml would have had the possibility to add the needed PPXs names to the source file (like Haskell's compiler extensions). So as to not have to read the Dune (or whatever build system is used) file to get to know where `foo%bar` or `[@@foo]` is coming from and what is doing. But at least the usage of `ppxlib` nowadays should make PPXs "compose" aka. not stamping on each other's feet.

https://ocaml.org/docs/metaprogramming http://ocamlverse.net/content/metaprogramming.html


I haven’t used it for some time but OCaml certainly used to have a zoo of incompatible compiler extensions. Circa 2008 or so I once hit on the brilliant idea of using protobufs to get two mutually incompatible halves of an ocaml program to talk to one another only to find that required yet another compiler extension to work.


Are you thinking of preprocessors? Back then, it would have been via Camlp4 and Camlp5.


Aah yes I am


I'm pretty sure F# was modeled on both. There are some definite "Haskell-isms" in F#; if nothing else, monads are typically done in something more or less equivalent to the `do` notation (an `async` or `seq` block), for example.

The syntax superficially looks a lot like OCaml, but it doesn't do the cool stuff with OCaml functors and modules; you write it a lot more like Haskell most of the time.


Here is the "official" history of F#: https://fsharp.org/history/hopl-draft-1.pdf

Don Syme began with a port of Haskell to .Net, but SPJ convinced him that this is a bad idea, so he did choose OCaml. ("The Decision to Create F#", Page 9)


You have to add extra syntax to do very normal things like have more than one expression in a function.


As someone who's coded OCaml for 20 years, I have no idea what you're referring to. `let x in y`? `x; y`? `x, y`? Those are all in the base language.


>NullUnderstanding compiler extension

brilliant! lol


Is brilliant the best word? :P


"now just sprinkle some `map . sequence . map`'s here and there and you are done. who said this was difficult?"




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

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

Search: