180 comments

[ 3.5 ms ] story [ 232 ms ] thread
F# is a great language, but the .NET community never embraced it.
It's a real shame, but not unexpected. The vast majority of programmers I meet in the wild simply do not have training in FP and by the time they decide to learn it (if they do), it's hard to unlearn "traditional" thinking.

If we want to steer programming in that direction, we should teach functional programming before "traditional" programming. However, even if we manage to do that I'm not convinced that it will make a different. People think procedural and it seems that the abstraction of first order functions is hard.

As a .NET developer I don't see any reason to use F# professionally. C#/CPP are tools just like a hammer, F# is a different hammer, it doesn't seem to offer advantages in my field other than being different...
The functional hammer can be a little easier to reason about. So you can understand your application a little more.
It's possible to write functional C# code, as well.
It’s possible but it’s just not the same.
Yeah you can. I find it a little more clunky. Many of the things I prefer are just syntactic sugar. But if I'm honest I really like syntactic sugar.

On top of that I find functional first languages are great for forcing you to learn functional styles that you can later user in languages like C#.

C# is a brilliant language and I wouldn't knock it. I'd use that as a first option at work but on a hobby project I'm going to play with F#.

Ehh, not really. It depends on how you define functional programming. C# supports first class functions, tuples, some form of pattern matching and LINQ extensions support basic FP collection operations like map (Select), filter (Where) and fold (Aggregate). But I think that's just scratching the surface compared to a "real" FP language:

- F# has function composition operators

- F# functions are curried and support automatic partial application

- F# variables and data structures are immutable by default

- F# has powerful global HM type inference

- F# uses option & result monads over exceptions and null

- F# has algebraic data types with exhaustiveness checking

These capabilities make writing code in F# a very different experience from C#. Some of these can be added to C# as features, but many things are fundamental parts of language design that are hard to change afterwards.

That stinks of microallocations from enormous stacks of closures, boxing and similar.
It allows you to envision your application as a series of data transforms, instead of a list of steps. This allows much more proximal abstractions for tasks involving data manipulation.
You can treat your application as a series of data transforms with C#, as well.
It's.. not as nice. LINQ gets halfway, but, you end up getting back to procedural modeling soon. With sufficient discipline and structure, and liberal use of LINQ and lambdas, you can mostly get there, but it lacks the purity of pure functional.
And in the process you've incurred a devastating amount of micro-allocations. When using Unity with an older version that doesn't have incremental GC this is a serious problem.

Does F# avoid that?

Oh, god no. Performance even on fairly trivial number crunching tasks is half of C#, nevermind anything where GC is important or you'd consider writing it native. It's not a performant language, that's not the point.
That's not a fundamental limitation, though. Implementations of SML (f# is effectively an ML implementation), OCaml, and Haskell are all quite performant.
F# generates weird, verbose, opinionated IL code that is often JIT-unfriendly and rather focused on reference types. JIT is tailored to C# patterns and JIT is rather simple in .NET (e.g. vs JVM), so it's easy to break or miss some optimizations.

Yes, it's not a fundamental limitation of the language itself. But considering the amount of hypothetical efforts required to fix all that vs investments from MSFT in F#, it is fundamental. F# lags by several years behind JIT and TPL improvements, the issues are mostly known. But it feels like it's not that important for F#. It's is not for writing fast libraries, but for Python-like use cases, explorative programming, or writing code fast when performance is not important (no hot loops in F# code) but strict typing discipline makes code less buggy and more maintainable from the first attempt. They used to say that "if F# code compiles, it's probably correct" - that is close to the truth, but do not expect top performance without fighting with the F# compiler. Even one of the best known and oldest F# library FParsec has it's high-performance parts in C# - that's telling.

> Even one of the best known and oldest F# library FParsec has it's high-performance parts in C# - that's telling.

To be more specific, it has a C# library so it can use _unsafe {}_ blocks to perform manual memory management. This is one key performance-oriented feature that F# lacks.

Other than that, you _can_ write F# that's about as fast as non-unsafe C#, although it means giving up most of the nicer and safer features of F#. Use mutable structs and classes instead of immutable records, for/while loops instead of higher-order functions, magic values instead of option/result types, etc... I've done it in a couple of hot paths where it made sense, but quite reluctantly.

Inline functions are still fine though for writing zero-cost helpers, and I don't think C# has them (there's an <AggressiveInlining> attribute but IIRC it's just a hint, the compiler isn't required to actually obey).

Ironically, F# _could_ become the best language for high-performance .NET programming, because it supports directly inlining IL instructions intermixed with regular code (whereas C# has to go through the ILGenerator class). Unfortunately it's considered such a niche case that it's not planned to be expanded beyond the standard library, and to use it in your code you have to enable an undocumented / unsupported compiler flag.

> Inline functions are still fine though for writing zero-cost helpers, and I don't think C# has them (there's an <AggressiveInlining> attribute but IIRC it's just a hint, the compiler isn't required to actually obey).

In F#, you cannot disable inlining of small functions. It does IL source code inlining, like copy-paste, not machine code inlining. That increases generated dll/exe size a lot for generics. And sometimes you do not want to do that because it's worse for performance. There is an issue for that: https://github.com/fsharp/fslang-suggestions/issues/838

In C#, the AggressiveInlining attribute works well, in predictable manner. Non-inlineable cases are well known (`throw\switch\fixed\try..catch\calling delegates` and some more https://github.com/dotnet/runtime/blob/master/src/coreclr/ji...).

> because it supports directly inlining IL instructions intermixed with regular code

This works for very simple things only. And using InlineIL.Fody, Sigil, raw IL.Emit or just raw IL code as text is not that more difficult if you already know IL.

> you _can_ write F# that's about as fast as non-unsafe C#

I tried this several times. It always ends with fighting the compiler more than just rewriting in C#, even without the `unsafe` keyword. In most cases due to generated IL that I cannot control from F#.

F# does a good job with tail-recursion, though - you can make the for/while loop safer this way. https://www.gresearch.co.uk/article/in-favour-of-recursive-f...
But it adds .tail prefix before every callvirt instruction and this breaks some JIT optimizations. E.g. generic type specialization 'if(typeof(T) == typeof(...))' breaks, I had to dig into compiler source code to find out that it doesn't add .tail before ctors, used a struct wrapper and called a method in the struct ctor to avoid .tail prefix, but it adds its own overhead.

Also, most simple tail calls are rewritten by a compiler to a loop, but if you look at the generated IL, there are multiple temp variables, locals, needless assignments. Maybe JIT is able to eliminate those, or maybe not... In C# I could get IL that reflects what I write, without surprises.

Slight correction: .tail prefix prevents inlining by JIT, it's mentioned in the second link in my sibling comment as "explicit tail prefix in callee". So even if that type specialization JIT optimization worked, the small performance-critical method containing it was not inlined. But the point of the specialization was to make the call for primitives faster than a method call, and fallback to a default implementation via callvirt. And there is no way to disable adding the .tail prefix to the calls that do not need it.

This also means that a lot of F# functions that are not inlined by F# source-code inlining itself effectively have NoInlining attribute. E.g. some helper method `if x then y.call() else z.call()`, if not inlined by F#, will never be inlined by JIT due to .tail prefixes before the methods calls.

You can use a teaspoon instead of a spoon to eat soup. That doesn't mean it's the best utensil for that specific task.
> proximal abstractions

Proximal? Maybe you mean appropriate? Or incremental?

Without the Visual Studio tools for GUI and database modeling, so it ends up being just usable for writing general purpose libraries.

So, we just carry on using VB.NET, C# and C++ instead.

Isn't 'writing general purpose libraries' most programming? I think GUIs and database modelling are pretty niche.
Code isn't written in isolation unless we are speaking about programming interviews.

Libraries spring to life as reusable parts from large scale projects.

So the ROI of context switching to F# in the middle of a project, isn't that much if the language is reduced to expressing algorithms.

I think one problem is (was?) winforms is not available in f#. (which is c# unique selling point i. m. h. o.)
Are you thinking of something else than winforms?

I believe winforms and WPF has always been usable but not with the visual designer tooling in visual studio.

UWP has been trickier though to my knowledge.

I probaby meant the visual designer tooling. (Which is a big point in using Visual studio)
I'm a long-time Emacs user, Scheme fan, functional programming advocate ... Who uses C# in his day job.

For quite a long time F# simply wasn't ready, and by the time it was C# had already become an industry behemoth.

But more critically, it's not a language that's officially supported by Unity. That's enough for it not to be a wise choice to be betting a project on it where there are dozens of jobs on the line, and families that could be impacted.

I can't help but think that this concern isn't simply isolated to Unity software development; where else has the decision been made to not use F# on account of it not enjoying as much industry support as C#?

I can't say that I've ever known anyone who uses Unity professionally but if that's your line of work it would be a big factor.
Sitecore, SharePoint, all the GUI designers for Forms, WPF, UWP, WinUI, EF (Core), WCF, gRPC, upcoming Code Generators.
I've done FP (several years of Erlang), and I use FP daily in my work (since most languages support FP to varying degrees).

I see no point in F# other than "well, it's a nice-ish ML for .NET", and the article does nothing to help with that (file order? really? that's what you're going for as the first point to make about a language?) .

Well, now it's also a "nice-ish ML" for javascript, thanks to Fable.
Agree, although C# is constantly evolving.
That certainly is playing a huge part in it. C# is importing more and more F# features so people aren't really inclined to switch.
When the mothership makes all the shiny toys only for VB.NET, C# and C++, that is bound to happen.
The love for F# is justified because it's really a clean language with a well-defined core library.

The one bad thing about it is that the documentation is really insufficient and confusing to people who don't already know .NET. If this improves it will definitely be welcome be more devs and companies.

I've spent the past 3 months or so working with F# with zero .net experience - I love the language alot but yeah, you will eventually end up learning C# (not that that's a bad thing) if you want to use its extensive and powerful ecosystem.

If you look at the MS documentation for .net, none of the examples are in F#. I guess that's okay since it probably wouldn't be idiomatic F#... but still, the docs have examples in C#, VBA, and one other language (I think it might be C) but no examples in F#.

Similar experience with Clojure, as well, one needs to know java to be reasonably productive with it.
I've been productive with Clojure for years and without needing anything from Java other than, say, a date library.
Not everyone is writing toy personal projects.
I'm learning F# now. The language is nice but pulling dependencies into the interactive shell is proving difficult. After a few attempts I found I could use #r to pull in dependencies. Then I found where the NuGet packages were hiding in my home dir. Finally I could reference the Fsharp.Data.dll! Then XML type provider not found....

Frustrating when I just want to play around.

Thank you!

Gah lots to learn.

:-).

F# tooling (e.g IDE support) is infuriatingly not as good as C#. Yet at the same time, the tooling is pretty decent, especially for a functional language.

I've been using F# 5.0's new features with dotnet interactive notebooks and the experience, when working correctly, is pretty nice. Dotnet interactive notebooks still has some rough edges to smooth out (and it's currently only available to VS Code Insiders) but, eventually, I think the new syntax coupled with dotnet interactive notebooks will make it a lot easier for people to try out F# and work through some problems/examples.
I like F# a lot but never used it past toy projects. Are there open source examples of decent Elmish WPF based production applications?
Love the idea behind the game engine for Xelmish https://github.com/ChrisPritchard/Xelmish

Its made on top of monogame and should have a battle tested core. But the lack of knowledge/adoption for Xelmish is disappointing.

Thanks for sharing this. I loved using XNA for many years!
> In F# the order of the files matter.

I didn’t quite understand this section and it seems very counterintuitive can someone elaborate a little?

In an F# project file (and thus also in all editor support), the referenced F# source files have an order. Files can only see types, values etc defined in files above them (and the same within files, with some caveats). This sounds really weird and annoying but turns out to have some surprising benefits (because it limits the mental/real search space for definitions as well). In practice it's just something that is surprising at first, then totally fine.
This guarantees that there are no cycles, but there are more ergonomic ways to do that. And the change would be backwards compatible.

I wish languages didn't insist on preserving quirks like this one.

That is pretty nice for testing, since it removes the need for depency injection, or am i mistaken?
All DI libraries can be ripped out and replaced with a bog standard main method, and it doesn't even require much skill. Most of what it's doing for you is stuff like "foo = new Foo(); bar = new Bar(foo, 33);" So there's no strict "need" for them, they help with (and enable) complexity.

What F#'s aversion to cyclic dependencies does forces you to break cycles of types referring to types referring to types. The way you do that is by writing a generic function that avoids referring to a concrete type. Because it's generic, such a function is inherently easier to test by means of a simple stub value.

Not really. There are various DI patterns you can use in F# [1], but the combination of no cyclic dependencies + type inference does mean you can just do the simplest thing - pass dependencies manually as specific arguments - and it will stay manageable for much longer than in java/c#.

Evil dangerous code using global variables:

   let mail = // .. create email service
   let db = // .. create database service

   let receiveThing thing = async {
      let query = // .. compose update query
      do! saveToDb db query
      let emailText = // .. compose email
      do! sendMail mail emailText
   }

   while readInput() do receiveThing (getThing())
Beautiful pure code using dependency injection:

   let mail = // .. create email service
   let db = // .. create database service

   let receiveThing db mail thing = async {
      let query = // .. compose update query
      do! saveToDb db query
      let emailText = // .. compose email
      do! sendMail mail emailText
   }

   while readInput() do receiveThing db mail (getThing())

[1] https://fsharpforfunandprofit.com/posts/dependencies/
I think he means you have to write down a list of files in your project somewhere, but each file can only depend on files that come before it in the list. So the files with the fewest dependencies (e.g. utility type stuff) come first, and `main.fs` (or whatever) comes last.
You do it in the .fsproj. I wish Microsoft would add a warning to explain that, instead of just saying the modules can't be found. Really confused me for a good few hours.
My general feeling is that, if you can generate a warning that tells you the right thing to do, then the compiler should just do the right thing.
That works right up until it doesn't. For example, if you have two interacting places where the compiler has guessed what to do, maybe the compiler resolves the interaction in the wrong way. It's the same problem that arises if you don't commit after merging into a Git repository (which, by the way, is possible but takes explicit opt-in): it keeps on working fine up until two previous merges conflict with each other.

I much prefer Rust's approach, which is to fail out but also make it extremely easy to apply the fix the compiler has guessed.

That's wrong on two levels:

1. The compiler doesn't usually exactly know the right thing to do. E.g. in this case presumably the error should be something like "You need to order the files so that each file doesn't depend on anything further down the list." The compiler doesn't know what that order should be (maybe in this case it could figure it out, but the point is more general than just this instance).

2. Sometimes the compiler might be able to figure out what you meant but you still did it wrong. If you accept wrong code then pretty soon people will start thinking that it is right, and then you have to support it forever. This is closely related to the thoroughly disproven "Robustness Principle", which actually leads to systems that are really not robust. HTML parsing is a good example (though mostly fixed today).

If F# ever got something akin to Scala Native, or Kotlin Native, I think it'd do really well. In my limited understanding of the F# ecosystem, I don't believe there are a lot of voices calling towards a multiplatform future beyond the NET world. Fable is cool, but native is a must-have for a lot of people, myself included.

The docs are bad but not inexcusably so, it's a nice project within MS, I get it. The underlying machinery leaks a little into the language, things like "discard" are irritating but not unforgivable. And it's not just running native, I know NET nerds will want to tell me about corert or native or whatever it's called, but that doesn't solve the issue. F# has a weak core, it's not like Clojure where I have this wide array to choose from regardless of the platform. I can choose NET or Fable and the development experience varies wildly between those two environments, in my limited experience.

All in all, I think F# will live a medium-length life. Without solving the native problem and moving beyond NET, I don't see it flourishing in many important niches.

If I was going to adopt a new language, it'd probably be Nim over F#, etc.

Isn't F# pretty much OCaml.NET? So if you want native, you may also go with OCaml (or ReasonML if you prefer curly-delimited code blocks).

For frontend dev't I really like Elm lately. (the article makes many refs to Elmish, the Elm-on-F# project).

Yes! Ocaml is quite good. Concurrency is kinda poor, but it's coming along.

I am glad that Elm has influenced so many ecosystems. I wouldn't touch it with a ten foot pole because of the author, but I think the ideas of the project are invaluable and long overdue.

concurrency is excellent in OCaml (Lwt, async, ....) it's just paralllelism that's a problem. multi core ocaml will eventually fix that.
Ah, of course. Clerical mistake on my part
You make it almost sound like having a fractured eco system with two different incompatible async frameworks is a good thing! :P
Could you please elaborate on what you said about Elm and its author? How come?
(comment deleted)
https://reasonablypolymorphic.com/blog/elm-is-wrong/

Here's a good starting place. Look for some discussions about typeclasses and read for yourself. Ask yourself if you'd bet on this person, and if you'd build critical tools for your org with a tool dictated by them.

I have no ill will for Elm. I'm grateful it exists and that we can all learn. I'd never invest in it though, it's too much a risk for things that could affect my ability to put food on the table quickly enough.

I'm not a frontend guy, so if some crazy frontend bug happened and crunched up my landing page or any of my forms, I'm not confident that I could debug it fast. I hate to say it, but honestly I think TS has to be the future of front end web. It's simply too solid to consider very many other options.

The one-man-running-the-show act kinda works for small things, like Tarsnap is a great example. But for languages that are powering business frontends, I can't have some goofball with a bad opinion holding up progress on something important.

Works like a charm, and when the community tanks (what will most likely be due to the close access innovation model and the main authors behavior), then there is a project to convert Elm code to PureScript. :)
The Elm architecture has been implemented in F# as "Elmish". It's quite nice!

I think Fable wins over Elm, simply because F# is such a great server-side language. Even if I was writing an Elm app, I would still likely reach for F# on the back-end.

similarly to how C# is very much java.net ??
This is a bit inappropriate since for a long time now Java was busy catching up to C#. When Project Valhalla merges, Java's type system will become very close to what C# is now: reified generics and first-class value types. Algebraic data types will also come to town. But only after Project Loom merges, Java will be firmly ahead again with a fresh take on all matters async.
C# also keeps copying Java stuff to this day like default interface methods.

In fact, having had spent most of my focus across Java, .NET and C++ since their early days, it is quite interesting to follow how certain features keep being copied across them.

But C# was literally Microsoft's Java implementation that they forked after they were sued by Sun, was it not? So it is quite appropriate.
C#'s development was motivated by their relationship with Sun, but J++ was MS's non-conforming Java implementation. They were sued for it, and basically couldn't develop it any further (with regard to maintaining Java compatibility, to the extent that they cared to at least).
Microsoft's Java implementation that got them sued was J++. As part of the settlement, they weren't allowed to add any new features to it, and it quietly faded into obscurity, along with its CLR counterpart J#, the latter having finally left even extended support in 2017.

C# was developed independently of J++ and J# (though J# was based on J++ to provide a transition path for J++ developers), and although Anders Hejlsberg (original lead architect of C#) did also work on J++, he also worked on Turbo Pascal and Delphi before coming to Microsoft, and I'd suspect that those influenced C# more (more conceptually than syntactically, of course).

I actually find C# to be more like "the successor to C++ that I actually want" rather than "Microsoft Java" at this point. If you approach C# as if you're just programming Java you are going to be missing out on some nice features, such as structs, unsafe blocks (including pointers), and modern additions like pattern matching. On top of that, I find that I prefer certain things about C#'s implementation compared to Java - reified generics over type erasure, for instance. Apparently Project Valhalla will change some of this implementation stuff. But for a while now I've found C# to be ahead of Java.

The syntax is also way nicer. Having to type out "implements/extends" instead of ":", for example. These goes a long way to denoise a very noisy language.

In the same way that Objective-C is just C++ for Macs. They share a common lineage, but do so many things so differently that you really can't consider them to be all that similar. And the difference starts with having completely different object systems.

Beyond different OOP mechanisms, some things OCaml has that F# doesn't:

  - Functors
  - Polymorphic variants
  - camlp4
And some things F# has that OCaml doesn't:

  - Computation expressions
  - Type providers
  - Quotations (as a built-in)
  - Units of measure
  - Active patterns
It is, but with a syntax that most people find less odd, and a couple of very compelling features, like..the ability to leverage more than one core, active patterns, and type providers.
.NET has native ahead-of-time compile options that are getting better all the time. F# actually works better with these tools than other .NET languages because it is less reliant on reflection etc. to build its abstractions.

You can write C bindings for F# libraries compiled this way.

I've toyed around on a weekend or two looking at the different ways to AOT stuffs for F#. Here is what I found:

Same hello-world console app from C# runs alright with AOT. The F# does not as it relies on heavier reflection stuffs (at least thats what it seems like). The F# version did compile but just crashed on run.

The suspect appears to be the way to console print seems to call a different non AOT function.

The interesting part was I did write some bindings calling for a console print via CFFI that did then allow me to compile the F# app and run fine.

Sounds like things have shifted since I last used it. Here's hoping they resolve this stuff soon!
Well neither Scala Native, nor Kotlin/Native are examples of world changing projects, so thankfully F# hasn't gone down that route.

In fact, Kotlin/Native is a poster child example of how not to design a language breaking the memory semantics with the rest of the eco-system.

Can you elaborate more on "In fact, Kotlin/Native is a poster child example of how not to design a language breaking the memory semantics with the rest of the eco-system."?
> Fable is cool

Fable is a game changer and is arguably a better Typescript in terms of jacking into the javascript ecosystem.

Ruby had Rails, Python had Django + Pandas.....

F# has Fable

how is fable better than ts, could you elaborate?
Firstly you can share code with .net and JS side. Secondly, you can do proper functional programming with F#. Third I find type system typescript as cryptic.
F# users generally avoid hierarchal type systems that plague TS/C# etc.

Don Syme has a great talk about this, but essentially things get out of hand very very quickly.

https://youtu.be/1AZA1zoP-II?t=2408

With Fable you are able to take the simplicity and conciseness of F# and jack into the entire JS ecosystem without falling into the type hierarchy nonsense.

I think the best way to think about such things is in terms of niches and ecosystem. The niche of functional and native is small and already well served by Haskell, Ocaml and even Rust to an extent.

The .net, python and JVM contain some of the largest ecosystems. Even if small relative to C#, the niche size and health of a functional language on .NET is much larger than if it were another native langauge. Speed, documentation and tooling might not exactly match C#, but are excellent in comparison to other functional languages.

In a similar vein, niche size for a transpiled functional language is much larger, more so if the language prioritizes playing well with its parent ecosystem. There is room enough for Fable, Clojurescript, elm, purescript and Reason to co-exist.

Concrete example: for my latest side project, I chose F# because it was the most compelling (to me) language with a decent option for cross-platform GUI that isn't Electron. (Avalonia.FuncUI, which has a nice Elm-y feel.) The reason it had a good cross-platform GUI library available in the first place is very much because it lives on .NET, and can build on of the rest of the .NET ecosystem.
Pure anecdote, but I've never seen a project become less native.

Meanwhile state of the art JITs (for .NET, JVM, Swift, and other traditionally managed runtimes) are increasingly blurring what it means for a language to be native and what the benefits for "native" even are.

So I don't think it's useful to think in terms of niches, because niches are constantly disappearing within our ecosystems. We're reaching a point where the implementation of a language is mostly a solved problem (throw it on top of some JIT for a bigger language) and the only meaningful innovation is in the semantics and syntax of a language for expressing intent and providing soundness.

If we're skating to where the puck is going here, I wouldn't say it makes sense to ignore native compilation for functional languages because that's a well served niche, but because AOT compilation itself is becoming niche.

Parser combinator libraries written in F# are phenomenal. All these libraries can be used along side C# in a project. Yes, .NET developers haven't embraced F# but I love C# and hate over complicatedness of Haskell so using F# is a pure joy for me.
I also like F# a lot and have used it professionally for 3 years now. I wrote down my own take on learning F# in a post "What I wish I knew when I learned F#" http://danielbachler.de/2020/12/23/what-i-wish-i-knew-when-l.... Maybe this is a helpful additional perspective for some.
Would you have if it weren’t supported by Microsoft?
Sure, we used Elm as well. But it sometimes helps in getting buy-in that MS is behind F# to some degree.
This is an excellent blog post! Thanks for writing it.
Complaining about bad docs isn’t really fair IMHO.

Compared to Swift F# has excellent documentation.

Would be interesting to know what F# is compared with in this case.

Is there a language thats known for excellent docs ?

One thing you can’t pass on when talking about fsharp in the community.

If you have a question just ask on twitter or slack. You’ll get an answer in minutes.

> Is there a language thats known for excellent docs ?

They're pretty good for PHP + Rust.

> Is there a language thats known for excellent docs ?

In my experience the Rust docs are great, there's a good mix of types as documentation, high level explanations and examples, with easy access to the source code. Here's an example with the HashMap [1]. There's also the Rust Book [2], which is great because it provides beginners with somewhere to start (F# seems to currently lack this).

The situation is pretty much the same with Elixir, great docs [3] and something to get people started [4].

[1]: https://doc.rust-lang.org/std/collections/struct.HashMap.htm...

[2]: https://doc.rust-lang.org/book/

[3]: https://hexdocs.pm/elixir/List.html

[4]: https://elixir-lang.org/getting-started/introduction.html

> Is there a language that's known for excellent docs ?

Mathematica's documentation is extremely good (reference.wolfram.com).

Since they came out, I've been craving type providers like Smeagol The Ring.
More leetcode trash. If you're not using a functional programming language you're "doing it rong".

Still yet to see practical advantages of using F# which don't result in convoluted, abstruse code... besides unit conversion for a calculator app

"...If you are sold with F# there is one important point to highlight. Do not treat F#, just another language with different syntax especially if you are familiar with Python, Ruby, JavaScript, C#, etc. You have to embrace functional programming as a paradigm...."

I want to provide a little nuance around this, because I'd give the opposite advice.

Because F# is based on OCAML and an extremely popular IDE/framework, it is first and foremost a teaching language. In my opinion, for what it's worth, the biggest mistake other F# coders make is running off into Haskell-land and trying to drag the rest of the community with them. Yes, pure FP is love and goodness, joy and love, but that's not the place to start with your average coder who just wants to see what's cool. Fable and Elmish are

I learned functional programming by reading a lot of OCAML books and the only(!) F# book out at the time. I coded like shit. I couldn't help it; I came from a strong OO background.

But then I figured out that instead of coding everything perfectly, it was more important to finish a little bit at a time, then take a look at functional smells, things like mutation. Sometimes I could fix these smells, sometimes I couldn't. Over time I found I could fix all of them. At that point I was a functional programmer.

I love these high-level F# frameworks, and like I said it's the best way to get folks involved, but wow, folks are going to create some programming disasters using them. Yes, this happens all the time withe every new thing, but functional messes are an order-of-magnitude worse than imperative/OO ones. Good luck, guys! The destination is worth the journey.

>functional messes are an order-of-magnitude worse than imperative/OO ones

Why so?

Symbol/Semantic leakage. In imperative/OO, I'm organizing as I go using well-established and understood paradigms. In functional programming, you're making these paradigms as you go along, or at least you should be. If you come in with an "I'm going to kick some ass by making these classes to solve my problem" attitude, you're already headed down the OO track whether you realize it or not. Then, sure as night follows day, you or somebody else starts using a functional paradigm, say HoF.

There's nothing wrong with any of that. As a professional, you want to be good in multiple paradigms. The problem is that you're mixing them all up, whether you realize it or not (Most of the time, programmers don't realize it) And now, every piece of code can go down two entirely separate tracks as you suss out your architecture. Not only is that not going to work, it's going to make debugging and maintenance a nightmare.

This is very, very similar to how good C++ shops work. You've got to tightly limit the way you solve problems in order for all of the programmers involved to be to reason about the code.

How are these points specific to OO vs. FP, instead of just paradigm I know vs. paradigm I don’t?

F# code, from my somewhat limited experience, seems to almost always be organized logically (top to bottom, types separated from functions) using well-established patterns. Obviously if you decide to write a bunch of classes and mutating code in F# when more idiomatic approaches would work, it’s going to get hairy.

I feel like I’m missing your point.

Happy to talk about it. Ping me. I've already been misunderstood a couple of times in this thread and I fear it's not going to get any better. After all, this is really more of a conversation than a point-counterpoint situation.
There are plenty of well-known functional language paradigms that have been discussed (and satirized) for decades.
IMHO it's tooling. The industry has spend decades to help you to deal with the kind of mess OO tends to produce. The tooling isn't anywhere close when you have to deal with a mess of functions returning functions that return functions.
> a mess of functions returning functions that return functions.

That is just a curried function with three arguments. The bigger problem is that programmers still have to understand how Hindley-Milner type inference works to figure out type errors. This gets worse when you add in type classes, dependent types and (Yog-Sothoth help you) lenses.

Your point is still true. For any language, it is important to have an IDE ecosystem in place. The tooling must enable developers to move beyond fighting the unruly or boring parts of the language and instead leverage it to work on the domain.

No, not necessarily. Curried functions have their own problems because they are missing the vital parameter names. But that's not what I meant. Think of the classic memoization example (written without a compiler at hand...)

    let createMemoizer f =
        let cache = Dictionary<_,_>()
        let f' = fun x ->
            match cache.TryGetValue(x) with
            | (true,value) -> value
            | (false, _) ->
                let result = f x
                do cache.[x] <- result
                result
        f'
    
The signature is ```createMemoizer (f:'a->'b) -> 'a->'b```, which is not very telling, especially if it is returned by yet another function, so that even the createMemoizer name is lost. In classic OO, I'd just get a Memoizer object that carries vital documenation and what not with itself, as it is passed around. It's no even that much longer, it's just not as elegant to use.

    class Memoizer<TINPUT,TOUTPUT> {
        Dictionary<T,TOUTPUT> cache = new Dictionary<T,TOUTPUT>();
        Func<TINPUT,TOUTPUT> f; 

        /* ... */

        public TOUTPUT Invoke(TINPUT parameter) {
            if (!cache.TryGetValue(parameter, out var result)) {
                result = f(parameter);
                cache[parameter] = result;
            }
            return result;
        }
    }
(comment deleted)
>functional messes are an order-of-magnitude worse than imperative/OO ones

I find them an order of magnitude easier and safer to refactor. And I have done it a few times with production messes I was not familiar with. Immutable values instead of variables and idempotent functions (those not having side effects) makes refactoring a joy and the if it builds it works mantra is pretty much true.

There is pretty broad agreement that pure functions and immutable variables are good to strive for. And since F# doesn't force you to do that even when it is a bad idea, things are quite nice. When mutating state is the simpler thing to do, you can do it.

Where things can get weird is when you start passing partially applied functions to other functions, and then you use type inference on function signatures on top of that. Glancing at the function signature its completely opaque what is going on. Hopefully you aren't in github and have working code lens and then you get a type signature to help, but it gets very verbose and confusing, and is still hard to reason about, for me anyway.

A thing I find helpful with F#, no matter what style you use, is always type out the types in function signatures. Yes this has downsides occasionally when a refactor might have just worked without adjusting types through a chain of functions, but I think it is a net win for productivity.

(comment deleted)
I've been doing almost nothing but F# for over 7 years, so I need to remember back to some of the things that used to throw me.

The signature never lies. Although I intellectually understood this early on, sometimes when I was in the throws of not understanding code my mind would not understand the signature or somehow would not grasp the important information it was telling me.

When they get a little complex and include generics they can get difficult for humans to parse.

The signature is truth. Stare at it, struggle with it until you get it.

One of the things that attracted me to F# is that every page of code is much more information dense than any language I had previously worked with. While I consider this good, it can also be daunting.

I had this happen with Rust. My Rust code as a beginner had a bunch of smells like unnecessary copies, overly verbose code, etc. But in the Maslow hierarchy of programming, working code is a prerequisite for idiomatic code. Better to write some bad, working code and slowly turn it into idiomatic, working code.
I also value F# for reasons that have very little to do with functional programming. I like the sum types, I like the generics, I like the syntax, but when I make things with it I do not get very functional, in fact I find the more aggressively functional approaches to be extremely confusing to reason about. Where functions take in partially applied functions and so on, it is very hard to read. Maybe thats fundamentally true, maybe it is just me, I don't know.
> Maybe thats fundamentally true, maybe it is just me, I don't know.

I think the ergonomics of a high degree of functional programming haven't panned out to be real crowd pleasers just yet, so you're not alone in thinking that.

I mean, `List.map` is often used in a partially applied manner and, used in conjunction with the pipe operator, results in, imho, readable code:

[1;2;3;4] |> List.map ((*) 2) |> List.reduce (+)

what would the equivalent code without partial application look like here?

Also, this is another pet peeve about 'functional style' F#, the use of actual Linked Lists, which is a huge performance pitfall to accept on modern hardware.

And also that F# Seq are not well optimized.

F# Seqs are .NET IEnumerables, which is to say they are iterators. They're lazy, and you can do things with them that you can't do with "strict" collections. In some ways they are faster than strict collections, in others they are slower. If you plan to be iterating over a Seq multiple times, you should probably turn it into a list or array first, just like in C#.

That example without pipes is

  List.reduce (+) (List.map ((*) 2) [1;2;3;4])
Linked lists are common in any functional programming language, the performance is bad but that only matters if you're writing something performance-critical. Otherwise, the ergonomics dominate: lists are very easy to work with.
I know what Seq is. C# IEnumerables already have more overhead than iterators in C++/Rust due to historical reasons that can't easily be unwound now, and F# Seq adds a bit more on top, it isn't horrible it is just too bad.

The ergonomics of LinkedLists really aren't any different than could be achieved with an array backed List, it just isn't done. I realize part of that is because it facilities immutable data structures, but they are often used when that isn't happening.

(comment deleted)
F# adds no overhead compared to IEnumerable<T> for comparable processing. Seq is a type alias, which is a compile time construct. Generating them is different as there are more ways to do that in F#.
> The ergonomics of LinkedLists really aren't any different than could be achieved with an array backed List, it just isn't done. I realize part of that is because it facilities immutable data structures, but they are often used when that isn't happening.

It still isn't as simple in implementation as a linked list, but they really shouldn't be used except as immutable data structures. That's the only reason I use them.

Phillip mentions this below but F#'s Seq does not add overhead to IEnumerable, it is exactly the same as IEnumerable.

I'm curious about what you see as the unavoidable overhead in IEnumerables, though.

> is first and foremost a teaching language

Terrific insight. So important.

As a Java partisan, I can say without reservation that Java is a suboptimal teaching language. So I easily believe your assessment of Haskell.

> I couldn't help it; I came from a strong OO background.

My functional eureka moment was deleting an element from a list using recursion. It literally changed how I see the world.

I haven't done any functional programming in anger (for pay) in a long time. But FP absolutely has informed, influenced all that I do.

Teaching multiple paradigms and styles is so important.

Are there any startups or companies using F# in production?
I just left a team that was using F# as our language for all new code. We built shrinkwrap banking software and found it a much better alternative to C#. We were the market leaders in that space, most of the worlds big banks runs code we wrote in F#.

I think F# is an improvement to C# just like how C# was an improvement to VB.NET, which was an improvement to VB6.

However, it's not really a hill I'd die on, frankly new co-workers complaining about missing curly braces gets really old after the first day. Ultimately most programmers just don't care about using the best tools, and would still be happy using VB6 if the industry as a whole hadn't moved them along kicking and screaming into C#.

If I'm working alone, I'll choose the best tools for the job, and I tend to think F# in modern dotnet core is one of the best tools for backend servers out there. It's much more coherent then Scala, less mushy at large codebase size than Clojure, very performant, and has great libraries like aspnet core, and compiles so much faster than Haskell and has an incredible wealth of better libraries. I also prefer a more functional style than Kotlin, Go, Java, or C# provide, because for me that means fewer unit tests to get the same confidence.

There's plenty annoying with the language, but it's a great hybrid with reasonable tradeoffs.

My elevator pitch typically is: with F# I need to write fewer unit tests to get the same confidence I'd need elsewhere, and that means more time programming.

Jet did, but they're now dead...or do they still live on inside of the greater Walmart (Labs?) organization?
Their F# projects under github.com/jet are still active at least. The main contributor/s appear to just have 'Walmart Labs' in their profile now.
Quant finance: G-Research (my employer).
As others have alluded to, it's more common in FinTech (ex: Jane Street and Quicken Loans). There also seems to be a number of front end devs using Fable + React.
>"You have to embrace functional programming as a paradigm."

Why would I embrace/commit to any particular paradigms? I would use whatever I believe is best suitable for particular task. All those are just tools like a screwdriver. They're here to work for us, not the other way around.

Sure, but functional programming requires some discipline and restrictions for a greater good. It's certainly a choice though.
>"Sure, but functional programming requires some discipline and restrictions for a greater good"

I do not care about some generic "greater good". It does not exist. In my context "greater good" is delivering products while satisfying business and technical constraints of a given customer/s to a reasonable degree.

F# on the JVM would be a game changer
I think the same thing about F# on Golang, for those tasks where GC latency matters.
> As you can see FSF offers mentorship programs

That made me blink.

And then I found out they meant the F# software foundation, not the Free software foundation.

It seems like a very obvious misunderstanding to make, and IMO they should avoid causing it by using another abbreviation than this one which is already very well established in programming circles.

I guess they don't. It's my mistake and I have just fixed it.
Is their a good F# book that will teach functional programing. Every F# book i see are trying to teach the syntax.
I am the author of the post and author of this https://www.udemy.com/course/learning-functional-programming...

Don't be deceived by low rating. It's low because people expected a course on syntax.

Also other continuations are as below:

https://www.udemy.com/course/functional-application-designin...

https://www.udemy.com/course/end-to-end-real-world-applicati...

And if you have any questions find me from @OnurGumusDev on twitter and I will gladly help

Thanks. Bought the course. Looking forward to learning.
F# For Fun and Profit has a lot of blog posts on how to think functionally and how to use F# for domain modeling.

Compositional IT has a lot of good blog posts on how they use F# to solve real world problems.

Kit Eason's Stylish F# is a good book.

(comment deleted)
Okay. I don't want to go too far off topic here but the OP is about F# so I'm certain many enthusiasts will be here, and I have been waiting for this moment to get an answer to a very specific question regarding Elmish:

What is the point of the Elm architecture?

I cannot figure out why the `model`, `view`, `update` architecture is preferable to its OOP counterpart. That is, simply defining an interface for each of the above where `IView` and `IUpdate` each contain a single method with the appropriate parameters. That is objectively[0] cleaner!

The current recommendation is to create these ungodly unions for handling the view/update logic. It feels ridiculous and really starts to gum up your modules with implementation logic that is _entirely_ unrelated.

FP and OOP each provide different (opposite) faculties when dealing with the expression problem. FP says[1], "I want to optimize for adding new _behavior_ to the same pieces of data" (one can add functions that operate on the same data without needing to recompile). OOP says[2], "I want to optimize for adding new _data_ with the same kinds of behavior" (one can add classes encapsulating different data that have the same operations without needing to recompile). How does the above square with an architecture built around a set of _single_ functions that operate on different (changing) pieces of data?

The Elm architecture seems to have chosen the exact wrong paradigm (and its associated semantics) for optimizing change! You aren't adding behavior as you build out your application in this architecture. You are adding _data_! The "behavior" is defined exactly one time (in the signatures of the `view` and `update` functions) and doesn't change. I must be missing something... is this really just so we can say "the complier will let us know if we forgot to handle a case"? That seems like a pretty hefty trade-off if you ask me. F# has this beautiful multi-paradigm capability and, in this case, it's been woefully ignored.

[0] Okay fine maybe not objectively, but it certainly seems preferable to modularize this logic.

[1][2] I get it. They don't actually say anything, but the general approach to the EP remains true for most cases.

OP here, it's a great question indeed! Now let's forget about elm/elmish but we are to write a stateful application. It doesn't have to be a web app, just any application needs to hold some state. And that state should be modifiable (Not to mention we will have side-effects as well.) in order to do something useful but we also want to stay on the Functional programming realm to get benefit from things like immutability and other functional goodies.

So you see these two goals are contradictory. How FP tackles this state problem? One solution is to use actors and agents. And that's precisely what elmish is. Unlike the conventional apps where you mutate the state directly, an agent in F# is a recursive call which can await for further messages.

So just like Flip Flop holds the state in memory, an agent hold the state in a recursive function. And how is this connected to elmish? Let's see how elmish v2 is implemented: https://github.com/elmish/elmish/blob/5330f52153d5181923bb3c... You can see an agent there and that's the core of elmish.

So basically elm and elmish is a way to handle state changes in a functional manner along with side effect support via commands.

I cannot emphasis importance of elmish, because not only it helps for isolating the state functionally but it helps you to keep your business logic separate from UI. So you don't end up messing your code like you use React hooks or context.

I understand what you are saying. I'm not sure how your points are at odds with the OOP counterpart I describe though. The essence of your response seems to be "because we want to use a pure FP paradigm" to which _I_ would add "despite a clear downside to which F# has the capacity to avoid".

If the framework only expects every `Model` to contain 2 methods (`view`, `update`) and never any more (which is the case), I see no reason at all to adopt the Elm architecture. The interface for `update/view/model` is well-defined and un-changing. It's clearly sitting on the wrong side of the expression problem.

The program loop could work exactly the same no? Just re-organize:

    let (model',cmd') = program.update msg state
to:

    let (model', cmd') = state.update msg // I'm not sure where/how program fits in
My question is truly as simple as trying to figure out the advantage of the chosen semantics (in F# - I don't know Elm). It can't possibly just be "because we want to stay on the Functional programming realm" can it? Immutability can be achieved in any paradigm. Forgive me if I am coming off overly controversial - I don't mean to be.

https://guide.elm-lang.org/webapps/structure.html - Even here in the "MVC" section the documentation _recommends_ you organize your project according to type (containing the `view` and `update` functions). This is very-much akin to my critique. If you reach the point in your project above you have essentially regressed back to OOP (in FP clothes).

let (model', cmd') = state.update msg

In order to write that you have to pin a reference to the state. But state is something that has to flow. Sure you can pin it to a ref, make it mutable and change the state but then nothing prevents you to change the state arbitrarily e.g. from a command. I believe yes the answer is because we want to stay on the FP realm. FP imposes some restrictions to keep our sanity. So not having a reference to the state and making it immutable is one of those restrictions.

> In order to write that you have to pin a reference to the state

Forgive my ignorance, but why is that the case? Isn't `state` being passed into the loop?

Mutability is orthogonal to the question of where one defines a unit of behavior. Maybe an immutable object is not possible to achieve in F#, but one could certainly imagine the concept of an immutable type with methods. My question is more about the modes of organization: Large unions vs discrete modules and how they affect how one's ability to change a program over time.

If you say that the architecture was chosen out of a purest sense of FP, then fair enough. I can understand why someone would go that route. Just had to know.

To the extent that I understand your complaint, there is a functional architecture that alleviates this concern: arrowized functional reactive programming. https://blog.jle.im/entry/intro-to-machines-arrows-part-1-st... talks about it.
Does this solve the problem though? I'll be honest, I'm not very familiar with Haskell (and frankly only casually use FP in general), but I don't see how the pattern in your link alleviates my concern.

The problem (truly the problem) is about _where_ one defines behavior. It's the expression problem (EP) at its core. A FP approach separates data and behavior in a way that makes adding new behaviors to the same data types easy and OOP combines them in a way to make adding new data types to the same behaviors easy. It's literally the difference between:

    let model' = update model msg
and:

    let model' = model.update msg
Like I said, I don't know Haskell very well (and had a hard time grokking the link), but any purely functional language will suffer from the former over the latter. The side of the EP a language falls on is simply as a matter of the data types/type system available.
How does this compare to BuckleScript or Reason?
Both BuckleScript and Reason target javascript and Reason Native targets mobile as well.

F# targets .net, javascript, and the Xamarin library targets mobile (although I haven't tried it yet).

They're all typed ml languages.

I know all of that.

Obviously I was asking about using F# to create web applications versus using BuckleScript or Reason.

The equivalent is fable [1].

The major difference I would say, is that you are required to use botnet alongside of node. (dotnet to compile to JS, then node to bundle with wepback or anything else)

Also, you can use the same language to build high performance backend with giraffe [2] (better than node). And build performant native application with fabulous [3].

There is an experimental typescript compilation. Which will be great, because it will make using F# less hostile in an environment where the winner takes all.

[1] https://fable.io

[2] https://github.com/giraffe-fsharp/Giraffe

[3] https://fsprojects.github.io/Fabulous

I've played with F#, OCaml, Haskell, Scala and Clojure. F# was by far the 'sweet spot' for me in terms of being able to translate my thoughts into a working functionally structured program. I hope many more are exposed to F# and that the good first experience is the norm (not currently from my experience).
Me too :). I think it hits the sweet spot for me between being as readable, expressive, and low ceremony as Ruby/Python but also type safe.
And the trade-off of type inference and 'you have to hint here' vs compile speed is also well worth it. Like other comments here, I usually add local names or types to signatures 'for readability' (esp. to future me).
it doesn't matter, it can't compile to a native executable

just like C#, it's dotnet after all, same engine

I am writing some elm now and I'm not extremely happy. How does elmish compare in practice? Elm has very good error messages and is mostly fast but it's also annoyingly limited.
I learn a lot by reading others' code. Can anyone recommend a a project or two that is small enough for a newcomer to wrap their minds around, that also exemplifies what makes a high quality F# codebase?