> Functional programming isn’t an afterthought in C#, it’s effective and you should learn it if you haven’t already.
I think C# is the best functional programming language because you always have access to a procedural code safety valve if the situation calls for it.
100% purity down the entire vertical is a very strong anti-pattern. You want to focus on putting the functional code where it is most likely to be wrong or cause trouble (your business logic). Worrying about making the underlying infrastructure functional is where I start to zone out.
Does F# care if a DLL it references was coded in a functional style?
> 100% purity down the entire vertical is a very strong anti-pattern.
It really isn't. The benefit of pure all the way down is that later you can replace bits with something more performant if necessary. But starting out with the idea that some bits should never be pure just means none of it is. The beauty of pure functional programming is that its very compositional nature means that you can replace a component without having a detrimental effect to everything its composed with: as long as you maintain referential transparency for that component.
What we gain from imposing the pure functional constraints on ourselves is genuine composition. If we compose two pure functions into a new function, that resulting function will also be pure. This is the pure functional programming super power that leads to fewer bugs, easier refactoring, easier optimisation, faster feature addition, improved code clarity, parallelisation for free, and reduced cognitive load. Opting out for arbitrary reasons at certain stages of the vertical threatens all of that.
There are a lot of patterns you get when you can be 100% certain of something. For certain functional languages, you could actually safely assume immutable patterns and such.
I really love C# but I will also say that 100% purity is a super power that C# will never have (can't have 'em all).
C# isn't unique in that, and it also doesn't offer the tools required to help avoid having to dip into procedural code.
Here's things Clojure, for example, offers:
1. Structural sharing which makes you less likely to have to dig in to procedural code.
2. The transient function which takes an immutable data structure and makes it... not immutable... you can now do your procedural code, then switch back into immutability land with persistent!.
3. If the above fails, you can dip into Java.
So yeah, I vehemently disagree with C# being the best. I could kinda see it if you said F# (type safety), but C# itself? Uhhh.
If you're going to write code snippets, please make sure they compile!
The final example has signature List<Product> but tries to return IOrderedEnumerable<Product>.
I recognise that this is very much a taster rather than even a full introduction, so the author didn't want to explain IOrderedEnumerable<T>, but please when writing blogs, run through your code examples and make sure they compile.
The second SQL-like version is far more readable. There is just less "syntax noise" and it's far more "declarative" by definition.
The author stating that the lambda is "better" because it's less lines is also silly. It's been a while since I've written C#, but pretty sure the SQL-like version can be formatted to a single line as well:
List<Product> GetExclusiveProducts(List<Product> source) =>
(from p in source where p.ProductTitle == "iPhone" orderby p.TypeOfPhone select p).ToList();
I'm the author of language-ext [1] a pure functional framework for C# and have been pushing the declarative programming thing in C# for over decade. C# is a great language for declarative programming and LINQ is awesome. You need to constrain yourself (and your team) so you don't fall into bad habits, but it can really pay dividends in terms of code stability/maintainability.
I have a couple of samples that I think might surprise you if you think C# must look like Java...
* A game of pontoon [2] - I create a Game monad which is an alias for a monad-transformer stack of StateT > OptionT > IO. This shows how terse you can get, where the code almost turns into a narrative.
* Newsletter sender [3] (which I use for my blog) [4] - This generalises over any monad as long as the trait requirements are met. This is about as declarative as you can get in C#. It's certainly pushing the language, but is still elegant (in my eyes anyway).
I love LINQ (the object method syntax not the keyword syntax) and used it extensively in a production application in ~2015-2017. It is easier in many cases to write and read than manual loops.
In performance-critical code at the time it had to be avoided due to allocations and poor performance compared to loop-based implementations.
However, the new versions of .NET have been reducing this penalty [1], to the point where it might make sense to try LINQ first if it's more concise/clear then only rewrite after profiling!
The example with lambdas should be written on multiple lines, too. At the same time, you can leverage the => syntax to avoid braces and end up with five lines:
(You could join the first two lines, but I think that’s ugly for multi-line expressions.)
Also, less lines is not a good argument for SQL-style syntax vs method-call syntax. The good argument is that the SQL-style syntax is limited to only a few basic operations, when there are many more useful methods available.
Another reason is that this does not compile:
List<Product> GetExclusiveProducts(List<Product> source)
{
return from p in source
where p.ProductTitle == "iPhone"
orderby p.TypeOfPhone
select p;
}
This method returns IOrderedEnumerable<Product>, not a list. To fix it, you would need to either change the return type, or go outside of the SQL-style syntax and call the ToList method:
> Your coworkers and QA will thank you for learning LINQ and ditching the imperative methods that plague your Python brain.
This is a very unfortunate joke: Python has list (and generator) comprehension expression for a long time (2.3?) which are similar to LINQ. At some point in the history many languages stole useful expressions from other paradigms.
I worked in a C# shop for a bit, but never wrote LINQ and saw very little of it.
However, in my Java days, I've very much enjoyed using streams. Looking back at LINQ now, it seems like a nice DSL around streams (which probably exist in C# land, too).
Once you start with LINQ you basically see it everywhere. It makes you think different about structuring your code, which is a good thing. It makes you think about immutability and pure functions, leading to more robust code.
But it’s also a fine line, when to use it and when not to. While LINQ is easy to read and make sense of, it is far from being easy to debug. Especially on inexperienced teams I tend to limit my LINQ usage and try to write more “debuggable” code. But that’s my approach, I would love to hear other peoples thoughts on it.
22 comments
[ 4.2 ms ] story [ 48.2 ms ] threadI think C# is the best functional programming language because you always have access to a procedural code safety valve if the situation calls for it.
100% purity down the entire vertical is a very strong anti-pattern. You want to focus on putting the functional code where it is most likely to be wrong or cause trouble (your business logic). Worrying about making the underlying infrastructure functional is where I start to zone out.
Does F# care if a DLL it references was coded in a functional style?
It really isn't. The benefit of pure all the way down is that later you can replace bits with something more performant if necessary. But starting out with the idea that some bits should never be pure just means none of it is. The beauty of pure functional programming is that its very compositional nature means that you can replace a component without having a detrimental effect to everything its composed with: as long as you maintain referential transparency for that component.
What we gain from imposing the pure functional constraints on ourselves is genuine composition. If we compose two pure functions into a new function, that resulting function will also be pure. This is the pure functional programming super power that leads to fewer bugs, easier refactoring, easier optimisation, faster feature addition, improved code clarity, parallelisation for free, and reduced cognitive load. Opting out for arbitrary reasons at certain stages of the vertical threatens all of that.
I really love C# but I will also say that 100% purity is a super power that C# will never have (can't have 'em all).
Here's things Clojure, for example, offers:
1. Structural sharing which makes you less likely to have to dig in to procedural code.
2. The transient function which takes an immutable data structure and makes it... not immutable... you can now do your procedural code, then switch back into immutability land with persistent!.
3. If the above fails, you can dip into Java.
So yeah, I vehemently disagree with C# being the best. I could kinda see it if you said F# (type safety), but C# itself? Uhhh.
The final example has signature List<Product> but tries to return IOrderedEnumerable<Product>.
I recognise that this is very much a taster rather than even a full introduction, so the author didn't want to explain IOrderedEnumerable<T>, but please when writing blogs, run through your code examples and make sure they compile.
This means that your audience can follow along.
( It just needs a .ToList() on the end. )
The author stating that the lambda is "better" because it's less lines is also silly. It's been a while since I've written C#, but pretty sure the SQL-like version can be formatted to a single line as well:
List<Product> GetExclusiveProducts(List<Product> source) => (from p in source where p.ProductTitle == "iPhone" orderby p.TypeOfPhone select p).ToList();
I have a couple of samples that I think might surprise you if you think C# must look like Java...
* A game of pontoon [2] - I create a Game monad which is an alias for a monad-transformer stack of StateT > OptionT > IO. This shows how terse you can get, where the code almost turns into a narrative.
* Newsletter sender [3] (which I use for my blog) [4] - This generalises over any monad as long as the trait requirements are met. This is about as declarative as you can get in C#. It's certainly pushing the language, but is still elegant (in my eyes anyway).
[1] https://github.com/louthy/language-ext
[2] https://github.com/louthy/language-ext/blob/main/Samples/Car...
[3] https://github.com/louthy/language-ext/blob/main/Samples/New...
[4] https://paullouth.com
The API docs are all auto-generated [1] so sometimes my repo markdowns get out of sync when I refactor!
[1] https://louthy.github.io/language-ext
Also better to pass IEnumerable to the function instead of List, for the same reasons
Also I forget the syntax but you can use an extension method to add it to linq I think
In performance-critical code at the time it had to be avoided due to allocations and poor performance compared to loop-based implementations.
However, the new versions of .NET have been reducing this penalty [1], to the point where it might make sense to try LINQ first if it's more concise/clear then only rewrite after profiling!
[1] https://devblogs.microsoft.com/dotnet/performance-improvemen...
... and then the author proceeds to presenting clunky, unreadable fluent syntax
That was a good joke for the afternoon.
Also, less lines is not a good argument for SQL-style syntax vs method-call syntax. The good argument is that the SQL-style syntax is limited to only a few basic operations, when there are many more useful methods available.
Another reason is that this does not compile:
This method returns IOrderedEnumerable<Product>, not a list. To fix it, you would need to either change the return type, or go outside of the SQL-style syntax and call the ToList method:This is a very unfortunate joke: Python has list (and generator) comprehension expression for a long time (2.3?) which are similar to LINQ. At some point in the history many languages stole useful expressions from other paradigms.
Let’s joke on BASIC, it always works.
Looking at that 'one-liner' I get strong perl vibes, or is it chills?..
However, in my Java days, I've very much enjoyed using streams. Looking back at LINQ now, it seems like a nice DSL around streams (which probably exist in C# land, too).
Interested in commentary around this!
But it’s also a fine line, when to use it and when not to. While LINQ is easy to read and make sense of, it is far from being easy to debug. Especially on inexperienced teams I tend to limit my LINQ usage and try to write more “debuggable” code. But that’s my approach, I would love to hear other peoples thoughts on it.