3 ms·
F# person here. Unfortunately I agree with you - the article doesn't really live up to the title. I'm going to assume that you're coming from C#. F# is unquesti
by dharmaturtle 5y ago
F# person here. Unfortunately I agree with you - the article doesn't really live up to the title. I'm going to assume that you're coming from C#. F# is unquestionably a superior language if you're doing LOB work. (Part of being an F# fan is knowing what it's bad at, and it's bad at game programming & perf compared to C#. This isn't to say F# is slow - but it's slow-er than C#.)
"Domain Modeling Made Functional" by Scott Wlaschin is excellent. It's in F#. His "railroad oriented programming" talk drove home the benefit of discriminated unions, which C# is... someday going to get. (It's been a WIP for years.) https://vimeo.com/113707214 https://vimeo.com/113707214
Avoiding side effects makes reasoning about code simpler. If you call a pure function, you know it doesn't effect anything else in the system. It doesn't mutate its arguments, it doesn't call a web service, etc. This means you can memoize it. What you see is exactly what you get. *In contrast, imagine writing code that must return `void`. The only way you can "return" a value is by mutating the arguments.* Now to be fair, eventually you must side effect in order to have any reasonable program (i.e. printing to the console is a side effect), but the goal of FP is to push side effects to the boundary of the system.
This also enhances testability. Your "core" is pure and easily testable, and your IO can have its own integration tests. My personal definition of "unit test" is a test of pure code. If it does IO, it's an integration test. F# is also wonderful in that it has FsCheck (and Hedgehog.Fsharp). Property based testing is a fantastic rabbithole to get into, and these days the majority of my unit tests are property based.
Where F# shines is when there's intrinsic, essential complexity in the domain. (Distinct from infrastructure complexity - F# doesn't have any real advantage if all your complexity is in IO). However, even something as simple as "user input validation" may count as domain complexity. For example in my domain, I have a discriminated union who's cases are `IdempotentError` and `UserInputError + string`. In the case of an IdempotentError, I just report things are groovy back to the user. For a `UserInputError`, I display the associated string. That string may/should be a type if you're starting to do internationalization. In C# you would need to do a ton of run-time if-checks or polymorphism to do the above. Yuck. In F#, these checks are enforced by the compiler. You literally can't forget to handle an error case.
Lastly, F# virtually eliminates all null errors. Its option type is wonderful, and C# only wishes its nullable reference was as simple.
I personally learned F# through Advent of Code. F# has a REPL which is ideal for these types of problems. Here's a blog and video about it: https://www.compositional-it.com/news-blog/a-brief-introduction-to-f-notebooks/ https://www.compositional-it.com/news-blog/a-brief-introduct... https://www.youtube.com/watch?v=_QnbV6CAWXc&t=1298s https://www.youtube.com/watch?v=_QnbV6CAWXc&t=1298s FYI F# notebooks is now out of preview, so you can use normal VS Code (and not the Insiders version).