What I wish I knew when learning F#

135 points4
eatonphil2 hours ago

I've had good experiences running F# on Linux. I used it to build an API generator from database schemas [0]. Similar to Go you can get a single static binary you can copy anywhere.

It's very convenient and you've got a massive number of .NET APIs to fall back on.

The language is a little complex though. That you cannot call interface methods on an object implementing the interface without explicitly casting to the interface [1] is pretty weird. And getters/setters are a little complex.

If you want an easy introduction to the ML family for educational/historic sake I'd always recommend Standard ML. But if you want a highly pragmatic, mature, strictly typed, compiled, cross-platform language F# is pretty compelling.



Multicomp3 hours ago

I am building a tabletop game trainer for Star Wars FFG as the RPG Sessions app is incomplete and trying to do all of the things manually or remember every rule + every modifier to the rule + what all of my NPCs stats are is impossible, and thus when one looks things up, it slows down gameplay a lot, especially during battles. Since the whole point of the system is to make battles feel fast and cinematic, and we want to strictly adhere to the rules as written so we are not constantly re-adjudicating (read: bickering over) house rules, we need something more.

Enter F#. I've been working on (yet not releasing a usable version yet bc I'm bad) this program entirely in F#, using Giraffe as the web layer, following the general architecture defined in Domain Modelling Made Functional, and using Dapper to write to a Sqlite database.

F# is amazing. I'm able to fully understand the codebase and follow exactly what happens from HTML Form request, through input validation, through the business logic, down to the database and back to the domain, then rendering out to a JS-free webpage via Giraffe.

It gets tedious doing the marshalling/unmarshalling from HTML form to input DTO to domain type to output DTO to database records to input DTO etc. and I need to learn both how to create DSLs (so users can type in rules from the book and the system will extract the keywords and compose them into the closest equivalent set of conditions and effects the system supports) and lock down my bounded contexts so I'm developing them in order of least to most entity dependencies rather than in order of where I feel I need them most, but I can't see a way where I would be able to build this in plain old C# without all of my above challenges + lots of boilerplate code to ensure my expected invariants are met and someone (me) hasn't changed an invariant out from under me somewhere else without realizing it.

2mol3 hours ago

Yeah, those boundaries between beautiful domain type <-> DTO <-> database is also something my team and I struggle with. In the end we're happy with just writing it out, but there is this nagging feeling that it could be more ergonomic.

I'd be interested to hear about other approaches. You could in theory just dump (automatically generated) JSON to the database and evolve your types with tagged versions, but then you have to write some boilerplatey converters between TypeV1, TypeV2, TypeV3, ...

I guess that's the price for staying away from full-blown ORMs?

It reminds me a bit of how in Elm you just suck it up and write the JSON en-/decoders by hand. Annoying at first, but then you get super obvious and maintainable code out of it, which is quite a joy.

de_keyboard2 hours ago

> It reminds me a bit of how in Elm you just suck it up and write the JSON en-/decoders by hand. Annoying at first, but then you get super obvious and maintainable code out of it, which is quite a joy.

The F# take on Elm coders (Thoth.Json) can use reflection to generate them automatically. This is really useful at the prototyping stage.

2mol2 hours ago

Yup, I love Thoth. At work we just use System.Text.Json + FSharp.SystemTextJson though, that also generates (de)serializers, and it has a bunch of options to fine-tune naming conventions, sum type encoding, etc. Super useful and easy to hammer out the correct type for very big and nested JSONs.

7thaccount3 hours ago

The biggest for me is needing to understand the .NET ecosystem without knowing C#. Then there's the lack of beginner information. I own several F# books and none of them assume you're coming from a scripting language background. I eventually gave up for good. OCaml doesn't have the .NET problem, but I found it to be unergonomic in a lot of ways.

Make no mistake, it's a cool language that I'd love to be good at. The code always looks elegant, I'm just terrible at writing it.

barrenko54 minutes ago

Kinda agree, but F# works much better for me with my knowledge of .NET than e.g. Clojure with my understanding of Java.

It's a cliche saying, but for me F# just works! (tm)

Decabytes3 hours ago

This was my issue when trying to learn clojure as well

billfruit3 hours ago

Yes, my experience as well. Clojure is more "lisp for java programmers" than "java for lisp programmers".

Most of introductory material I found was good in explaining functional concepts, lisp approach to solving problems etc, but I thought assumed a working knowledge of the JVM ecosystem.

For someone utterly unfamiliar to Java/JVM world would find clojure extremely baffling, cryptic error messages, needing to be familiar with the rather large java standard library, etc.

pjmlp2 hours ago

That is the issue with any guest language, because it is impossible to hide the platform, unless the language is so constrainted that is practically useless.

It is a hard to swallow truth, but guest languages really require understanding of the underlying platform.

Another example, no matter one's opinion on C or JavaScript, mastering them is a much more confortable life on UNIX/POSIX platforms and browsers than trying to pretend they aren't there.

7thaccount3 hours ago

Same. F#, Clojure, Scala, & Kotlin have all been the same for me. Python's VM is written in C, but I have no reason to know C. In Clojure they have you call out to Java libraries a ton. Someone always tells me that this isn't true on here, but it was my experience as well. Having to learn so many tools like emacs or CIDRE or Leinegen was also a turn off.

gorjusborg2 hours ago

I really love Clojure, but I have a lot of experience on the JVM so I didn't have to climb multiple learning curves simultaneously.

While the clojure ecosystem has a lot of wonderful, bright and helpful people in it, and clojure itself is technically impressive (esp. its collections implementation), the new user experience isn't great.

I'm not an experienced emacs user, so that makes it doubly difficult, as the ironed-out workflows seem to assume that you are (which is fair, as emacs and lisp are like peanut butter and jelly). However, these days it's fair to assume that a new clojure user is probably learning 'clojure', 'lispy ways', 'java/jvm', and 'emacs' simultaneously.

I think that calva/vscode has a lot of potential to drop emacs off that list, which is welcome, as its learning curve alone is legendary. If you look at that list, it should not be surprising that the community is small. There's so much to learn that I it seems too much to even start.

kaliszad40 minutes ago

There are multiple options to approach the Clojure* ecosystem. But yes, it is more focused towards dedicated and experienced developers overall. I use IntelliJ Idea + Cursive for development and that seems quite comfortable. I have a very rough sketch of the environment setup for a real app here: We use a similar setup to develop OrgPad itself.

You can use Babashka for a quick and dirty setup to get you up and running quickly. That is also useful for scripting some things. You might also try shadow-cljs + ClojureScript, if you are more at home in the Node.js/ Browser JavaScript ecosystem and want to do something with it. There is a new "(Not)Babashka" but built on ClojureScript: that might be interesting as well.

I have found this video by James Trunk: to have a very nice live coding example.

barrenko52 minutes ago

Clojure is pretty cool, but I've come to understanding that as a beginner I would have been better off just learning good old Lisp.

rixkys2 hours ago

I'm surprised, I found clojure even while I was a pretty much total beginner programmer fairly easy to get into, via clojure for the brave and true at least was very good for getting the basics.

WillPostForFood31 minutes ago

Clojure for the brave and true shows the exact problem.

Chapter 1: install and learn the JVM

Chapter 2: Emacs

Chapter 3, finally start learning Clojure if you made through the first two chapters. Those first two chapters are going to filter out quite a few potential Clojure learners.

7thaccount44 minutes ago

Even that involves downloading and installing a fair amount of build tool stuff which may be necessary, but just drove me away.

DeathArrow2 hours ago

There are several good books like: and and also some courses on Udemy like: and

Also there are lots of learning resources here: some for guys coming from scripting languages like Python. As a matter of fact, F# can be used for scripting, too.

7thaccount46 minutes ago

I'm not saying that you can't write an F# script, but that the path to getting one written is not obvious to those coming from scripting languages that are much simpler than the VAST .NET & JVM ecosystems. There is always some bizarre .NET arcana that gets in the way.

orra3 hours ago

Ah, that's interesting.

I'm a huge fan of F#, but I had the advantage of knowing .NET first.

bob10293 hours ago

If you want to dabble in functional, C# is actually a compelling option now. Switch expressions and LINQ can take you a long way.

I strongly believe that functional programming is not a good fit for 100% of software architecture. The best is some sort of hybrid. Generally, the closer you get to the business logic, the more functional you would want to be. The serializers, http servers, etc. are probably not worth the squeeze to force into a functional domain.

grumpyprole3 hours ago

The creator of LINQ, Erik Meijer, wrote a fun article "The curse of the excluded middle: Mostly functional programming does not work". Yes it's tongue-in-cheek and highly provocative, but many of his points are true. There is a lot more yet to be learned from functional programming than LINQ and switch expressions.

aranchelk2 hours ago

Is it meant to be tongue and cheek? I personally didn’t get that impression. I assumed he was using the non-religious meaning of “fundamentalist”: strict and literal adherence to a set of principles.

de_keyboard3 hours ago

> The serializers, http servers, etc. are probably not worth the squeeze to force into a functional domain.

I used to think the same, but having now tried FP-style libraries for serializers (Thoth.Json) and HTTP servers (Suave), I think they are far superior to imperative or OOP alternatives. The ability to design these things in a declarative style makes the code much more robust and easier to read.

dustingetz3 hours ago

FP is better on a 30 year timeframe but on a next-quarter timeframe probably still has not "crossed the chasm" – you need not just something accessible like ZIO but you need to figure out what it's killer app is, which the FP community currently doesn't have a clue. "HTTP server but more declarative and made with monads" is not something people cannot live without

de_keyboard2 hours ago

I think this depends on the relative weights you assign to learning a new language and program correctness.

JaggerJo3 hours ago


mumblemumble2 hours ago

I agree that 100% functional is not necessarily the way to go. That said, I am a big fan of F#'s "functional first" ethos, which drives you toward that sweet spot of a functional core inside of an imperative shell.

With C#, when I'm trying to use functional design, I often feel like I'm swimming against the current. The language has functional features, and it will certainly let you use them, but, LINQ aside, the path of least resistance is mostly imperative and object-oriented.

In F#, it's the other way around. It has a full suite of object-oriented features - still, after all these years, quite a bit more complete than C#'s suite of functional features - but the path of least resistance is mostly functional.

I don't want to say that's a universal best way to have things. But it suits my taste, because it makes the easiest way to do things correspond very closely with the way I like to see things done: distinct and well-distinguished layers of functional and object-oriented code, sorted according to where each is of the most utility. C#, by contrast, tends to guide you toward a foamy lather of functional and object-oriented code with no identifiable organizing principle.

barrenko50 minutes ago

This is the same issue I have with Javascript. If you don't have to use something, you just won't, or maybe I'm just lazy.

ulucs3 hours ago

I think serializers would be better off as railways, allowing you to combine small serializers while also collecting the errors so you end up with a conclusive outcome. Haskell's parser combinators are quite fun for example.

Also I've only used node/elixir serverside, but aren't http servers just a huge pipeline? It fits quite well into functional programming.

bob10293 hours ago

> aren't http servers just a huge pipeline

Absolutely. At a certain level of abstraction they can be viewed in this way.

How do HTTP servers actually get packets to and from the machine? At some point you have to interact with the operating system. This is not a realm where functional programming is very feasible today.

Koshkin3 hours ago

Sure - C# is the best functional language, while Haskell has already been the best imperative language for a while now [0].


GuuD3 hours ago

I would have to respectfully disagree with you on the serializers part here. For me, after decades of fighting with “magic” stuff, simplicity is the key feature. So, everything that is a simple map function in disguise I tend to stick with functional languages and approaches.

ghostwriter3 hours ago

> I strongly believe that functional programming is not a good fit for 100% of software architecture. The best is some sort of hybrid.

Haskell would be a better language in every domain where C# is applicable. That is from a language perspective, abundance of libraries is a different question, as it's a matter of the community size and attention.

kevinmgranger1 hour ago

I think it's really hard to properly define "functional programming" concretely, but I'll partially agree. If all a language takes from FP is:

- make composition ergonomic, idiomatic, and easy (including the usual iterator map/reduce, etc.)

- let the type system protect you (including nullability)

Then you've already got the most important strengths, in my opinion.

wyager2 hours ago

> The serializers, http servers, etc. are probably not worth the squeeze to force into a functional domain.

These are two examples I would probably come up with if you asked me “where does FP thoroughly beat imperative?”

(De)Serializers - parser combinator libraries like attoparsec absolutely BTFO imperative parser libraries in most dimensions. Bidirectional codec libraries like haskell’s Binary, Serial, Aeson[1] are top-tier. Functional formatters like Blaze are top-tier as well.

Haskell’s HTTP ecosystem is massively better than anything else I’ve used, and I went on a binge of trying a ton of HTTP servers like 6 years ago (in python, ruby, C++, Go, and finally Haskell).


AtNightWeCode1 hour ago

In my area there was a huge push for functional programming and F# some years ago. A long time ago since I heard anybody working with F#. Clojure is somewhat popular though in fintech.

Functional programming is something I think one should learn cause the concepts are useful in other programming languages too. Over time what is useful in functional programming languages will be available in other languages. In fact, a lot already is. Functional programming is not an USP in my world.

When I started with C#, code bases were full of GoF patterns, if and switch statements were banned, there were hierarchies of inheritance, there were states spread out and encapsulated everywhere, static functions were banned. Today, code bases are mostly stateless except for things that needs to be reused, there are pure functions everywhere, inheritance is mostly used in libraries and where interfaces can’t be used (yet), switch is ok and pattern matching is around the corner in several languages.

More unpopular opinions. Immutability is overrated. In some programming styles, in some old languages there was a high risk of changing something by accident. Typical cause people did not know if they were working on a reference or a copy. Also, it was common to change fields on purpose. This problem does not really exist in a lot of programming languages today. But I do wonder if Go coders code benefit from some more immutability.

I think the future of F# looks very dark but the functional style of programming will for sure have its place. What could change this is if F# really excels in some area like Go have done.

muglug3 hours ago

> It has a scripting mode where compilation is done on the fly. This makes it look and feel very similar to python but you still get type checking and much better performance.

This is a really wonderful quality in a compiled language. I wish there were more languages with this attribute (but it’s a lot to ask for).

BBC-vs-neolibs3 hours ago

Crystal, the compiled-but-super-fast statically typed Ruby dialect.

JayGuerette2 hours ago

His claimed inspiration, Hillel Wayne’s post “Why Python is my Favorite Language”, is a piece of fluff that basically says "I only like the things I already know". I was so uninspired, I couldn't read further.

infogulch2 hours ago

There was a recent thread about Algebraic Effects in OCaml [0]. Does F# have something similar?


Smaug1232 hours ago

No. It has lightweight syntax ("computation expressions") you can use to sugar monads, but no effects system.

mickjagger3 hours ago

What does F# do that other languages don’t? What it’s unique benefit?

Smaug1233 hours ago

It's just really pragmatic about the upsides of functional programming. It describes itself as "functional-first", but isn't afraid to fall back on OOP in situations where OOP really does model the situation better.

In my mind, it's taken the easiest 80% of the features of a true functional language, but then picked a pragmatic point at which to stop; the result is a language which is easy to learn and also makes it easy to use "most" of the good things you want from a functional language.

mjul3 hours ago

Type providers are novel.

It is a mechanism to provide type safe access to structured data and data services with almost no code. It feels like magic.

Like other ML-derived languages (F# is an OCaml dialect) it has algebraic data types and pattern matching to make code simple and complete without missing edge cases.

Also, it is arguably the best functional language on .NET, so it has a great platform with a large ecosystem to build on. And the tooling is great (Jetbrains, Microsoft IDEs).

aloisdg4 minutes ago

> it is arguably the best functional language on .NET,

it is also the only real one. On .NET we have C#, C++ and then there are some other fringe langs like IronPython or IronRuby and I think there is a PHP one too.

runevault3 hours ago

I haven't done as much as I would like with them, but I feel like until someone tries type providers they don't get just how powerful they are. Being able to point to an example .csv file and have it generate the classes for you, and update them if you change said format, in a way that it ensures the types still work, is amazing. And you can do it for ANYTHING as long as you have a way to extract the data to feed it to the compiler.

I still need to learn how to write a type provider just so I can.

de_keyboard3 hours ago

It's the combination of features which is killer. I don't know of another language that offers all of these:

- Expression orientated

- Lightweight syntax

- Emphasis on immutable data and pure functions

- do-notation (in F# these are called Computation Expressions)

- Type-safety without too many annotations (global type-inference)

- Mainstream ecosystem of libraries (in this case .NET)

- Compile to JavaScript

brightball3 hours ago

I believe it's the functional language option on top of .NET. From what I understand, it's a pretty well designed one too.

I don't work in the .NET ecosystem, but I know if came up a lot when I was getting into Erlang/Elixir.

DeathArrow2 hours ago

Functional, readable, easy to learn, as performant as C#, has the benefit of huge .NET ecosystem of libraries.

barrenko50 minutes ago

When I learn Haskell, I'll be lot closer to God.

codingclaws3 hours ago

This is off topic. I can't resist - that's the thickest sticky header I've seen from a developer.

gorgoiler57 minutes ago

Yes, hah, I too was impressed with the chutzpah of having…


…on a substantial amount of my screen throughout the whole read.

StreamBright2 hours ago

F# has become my default goto language. It is absolutely amazing to create cloud services in it an I have a small ETL tool as well. I would not trade it for anything else.

jgwil21 hour ago

Link to What I wish I knew when learning Haskell is broken because it has https. The correct link is in case anyone was looking for it.

ur-whale3 hours ago

Is there a FOSS compiler for F#?

Smaug1233 hours ago

You can simply contribute to, the maintainers are very friendly to new contributors in my experience (though the codebase is old and often quite hard to understand).

gameswithgo3 hours ago

the Microsoft .net core compiler is FOSS, but if that doesn't count there is also mono.

ur-whale2 hours ago

> the Microsoft .net core compiler is FOSS

Oh, I didn't know that.

Very cool.

Can I apt-get the toolchain?

[EDIT]: and for those interested, here's the github repo:

ddek48 minutes ago

You can, but I wouldn't. The dotnet CLI contains its own version manager, so you need to have more precise control over the versions of the runtime & sdk you have installed. You can have multiple versions installed simultaneously, dotnet then chooses the runtime based on the project. IME linux package managers mess this up bad.

Fortunately MS provides a better alternative: the dotnet install scripts. [0] Make sure you add the install directory to $PATH.

[0] -

ur-whale42 minutes ago

> so you need to have more precise control over the versions of the runtime & sdk you have installed

Oh, this doesn't sound good at all.

One thing I positively hate about Java is always having to worry about which version of this and that (sdk, vm, headless, etc...) you run on top of. The claim of write once run anywhere has not really happened in my book.

Even C++ is better in that regard these days.

I am certainly hoping the .NET / F# environment didn't inherit that attribute from the Java ecosystem, that'd be a major downer.

My goal is only to learn F# / experiment with it, and I'm certainly hoping never to have to spend a second thinking about "versions of the runtime & sdk".

orra2 hours ago

> Oh, I didn't know that.

In fact, F# has been open source longer than Roslyn (the C#/VB.NET compilers) and .NET Core!

There used to be a funny split in the repos: Microsoft had their own visualfsharp repo, and there was a blessed fork which was more community and Linux friendly.

DeathArrow2 hours ago

Yes, you can.

ur-whale2 hours ago
BBC-vs-neolibs3 hours ago

... to be clear, Mono does not provide F#. Microsoft releases F# for Mono.

steve7616 minutes ago

> Scott Wlaschin’s fsharp for fun and profit is one of the best functional programming resources I know across all functional programming languages.

Hey! Someone else saw this. Very underrated resource. Also Professor Frisby's Mostly Adequate Guide to Functional Programming needs more attention too.

Things I would add:

1. Start off with what types you need, not functions. Creating your types like the tools you need to do the job. Then build combinators. Convert one complex type to a primitive. Then a primitive to your type.

2. Once you have your types, it's not that difficult to make them functional. You just make methods for "of", or "chain", or "map", or "ap". Now you have monadic and applicative interface.

3. The functional part is just like when a shell drops to a program's prompt, like >ftp and >mysql. You setup a series of commands like that, with Either or Task or Fold. Provide input, prepare a list of commands, and then run them when everything is valid and ready.

4. Bundle size is a problem for web. Until tech improves and bandwidth prices lower, 100 KB on a high volume website is a problem. Nothing wrong with taking a proven solution, like wordpress, and caching it.

5. Property based testing is for under the hood, your types and combinators. It's not something you do with Cypress or units. All that input is type unknown, and needs validation before going to what's coverage under property based testing.

6. One fault that always occurs is trying to use Runtime encoding to build some extensible modular program. Don't do that. Types are for build. Validation is for runtime. If it doesn't validate, show a user a message. Don't try to build some hot swappable modular program, where types are read from input. If they want to program the computer, they can install the IDE.