HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
louthy
language-ext
C#2014–2026
louthy / language-ext

language-ext

C# pure functional programming framework - come and get declarative!

20Distinct people
109Mentions
1Explicit recommenders
7.1KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

C# pure functional programming framework - come and get declarative!

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

I take this approach in my language-ext project [1]. I wrote it because as well as wanting to use F#, I had a decade old C# project to maintain, and I wanted to bring…

louthy used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 8–Nov 15 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/15/2026, 2:59:58 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year109 dated mentions

Observed mentions: 2014-11-19 17:06:14 UTC to 2026-09-16 19:56:17 UTC.

051015202014-01-01 00:00:00 UTC to 2015-01-01 00:00:00 UTC (exclusive): 1 mentions; 1 submissions, 0 comments20142015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 6 mentions; 0 submissions, 6 comments2016-01-01 00:00:00 UTC to 2017-01-01 00:00:00 UTC (exclusive): 21 mentions; 2 submissions, 19 comments2017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 14 mentions; 1 submissions, 13 comments2018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 5 mentions; 0 submissions, 5 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 12 mentions; 0 submissions, 12 comments2020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments20202021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 7 mentions; 1 submissions, 6 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 10 mentions; 0 submissions, 10 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 6 mentions; 0 submissions, 6 comments2026-01-01 00:00:00 UTC to 2027-01-01 00:00:00 UTC (exclusive): 8 mentions; 0 submissions, 8 comments. Incomplete calendar period.2026*

* An incomplete calendar period is marked with an outlined bar. Its count is not directly comparable with a full period.

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20142014-01-01 00:00:00 UTC2015-01-01 00:00:00 UTC110
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC606
20162016-01-01 00:00:00 UTC2017-01-01 00:00:00 UTC21219
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC14113
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC505
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC12012
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC808
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC808
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC303
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC716
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC10010
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC606
2026 (partial)2026-01-01 00:00:00 UTC2027-01-01 00:00:00 UTC808

What Hacker News says

6 sources
This [1] might help 'get it' with regards to monads. Very easy to digest. You mention LINQ and the List abstraction. Yes IEnumerable<T> is a monad (LINQ isn't in itself - it [the grammar] is the equivalent of 'do' notation in Haskell). Monads are simply 'wrapper types' that follow a couple of rules: 1. You must be able to construct one from the un-wrapped value (return in Haskell, new List<T>(...) in C#) 2. It must implement the bind function. If you understand 'map' (or Select in LINQ) then bind is very similar, except instead of returning a mapped version of the wrapped value, it returns a mapped version of the wrapped value, re-wrapped. So in C# parlance (because I assume from your comment you use this daily): IEnumerable<R> Map<T, R>(this IEnumerable<T> self, Func<T, R> mapper) { foreach(var x in self) {
Read the rest
yield return mapper(x); } } IEnumerable<R> Bind<T, R>(this IEnumerable<T> self, Func<T, IEnumerable<R>> binder) { foreach(var x in self) { foreach(var y in binder(x)) { yield return y; } } } You may recognise that as SelectMany in LINQ. Select and SelectMany are special case function names in C# that allow the formation of LINQ expressions: var res = from x in list select x + 1; Equates to: var res = list.Select( x => x + 1); And: var res = from x in list1 from y in list2 select x + y; Equates to: var res = list1.SelectMany(x => list2.Select(y => x + y)); The result is a 'flattened' IEnumerable<T> - and that's why this is also known as flat-map. It's been a while since I've done any Haskell, so forgive me if I get some of the syntax wrong here. But the LINQ statement above translates very similarly: do x <- list1 y <- list2 return x + y (I know there's a list comprehension syntax in Haskell, but I assume this is still right, Haskellers?) The syntax is saying 'get the value out of its wrapper (the list) for me, so I can use it as-is (the add operation), then put the result back in a new wrapper'. So why do we do all of this? It's so you can write the functions once that behave on the wrapped types, whether they're integers, strings or any type. The monad concept allows you to create a 'chain' of behaviour that from the outside is opaque, but internally it's operating on the raw wrapped values, as-is; and that makes all of the existing functions available. This is one of the key benefits. A good analogy is to call them 'programmable semi-colons', because what makes each monad type what it is, is the behaviour of bind and return. So a list monad (as seen above) knows how to iterate the collection - so you don't have to write a for loop every time, an option monad knows when to not invoke the bind function if it's in a None state - so you don't have to test it yourself with an 'if' every time, the state monad knows how to propagate state changes - so you don't have to have extra context arguments on every function down the hierarchy, etc. They remove boilerplate and capture common patterns. That is the other key benefit. At its core is an abstract idea, and I think that's why it's sometimes quite hard to grasp; but it's just a design pattern. Forget the category theory side of it, you don't need to know any of that to use use them or write new ones. If you're more used to C# than Haskell, then you may want to check out my 'C# functional language extensions' library [2] that has C# implementations of the following monads: Option Either State Reader Writer And a few others, but that should be enough to get started. It may help you get your head around it in a language you're more familiar with? [1] http://adit.io/posts/2013-04-17-functors,_applicatives,_and_... [2] https://github.com/louthy/language-ext
In Reflecting on Haskell in 2015
Whilst non-nullable references are useful, that isn't what Option is trying to solve. The problem with null references is that when you're handed a reference to an object of type T - the type system is saying to you: "This is the contract I will honour". Except it doesn't when the reference is null. However the functionality of a null reference type is useful, because it allows a function to return 'no value'. So the goal of an Option type should be to expose the contract for T when there's a value and remove the contract when there's no-value. The programmer is then explicitly forced to acknowledge the two states that the value could be. Shameless plug - My C# implementation of Option which is possibly the most complete example out there (at least in C# world): https://github.com/louthy/language-ext/blob/master/LanguageE...
In Java 8’s new Optional type doesn't solve anything
Whilst some of the issues with C# are valid, it's totally possible to do monadic error handling in C#, I wrote an entire library to facilitate that [1]. In fact the big win of monadic error handling is the composable nature of monads: Here's a function that throws an error on odd numbers: Try<int> Number(int x) => () => { if( x % 2 == 0 ) return x; else throw new Exception("Any exception"); }; Here the monadic computation succeeds, and 'res' holds 1000: var res = match(from x in Number(10) from y in Number(10) from z in Number(10) select x * y * z, Succ: x => x Fail: 0); Here the monadic computation fails, and 'res' holds 0: var res = match(from x in Number(10)
Read the rest
from y in Number(9) from z in Number(10) select x * y * z, Succ: x => x Fail: 0); If you don't want to use it in a LINQ expression then you can just say: var res = Number(10).IfFail(0); There's also mention of the null problem, which obviously is a major problem with the language (and framework), but it can be tamed somewhat with Option<T> and TryOption<T>: TryOption<int> Number(int x) => () => { if( x % 2 == 0 ) { if( x > 10 ) None; else return Some(x); } else { throw new Exception("Any exception"); } }; Here the monadic computation succeeds, and 'res' holds 1000: var res = match(from x in Number(10) from y in Number(10) from z in Number(10) select x * y * z, Some: x => x, None: 0, Fail: 0); Here the monadic computation fails, and 'res' holds -1: var res = match(from x in Number(10) from y in Number(9) from z in Number(10) select x * y * z, Some: x => x, None: 0, Fail: -1); Here the monadic computation also fails because a None is returned, so 'res' holds 0: var res = match(from x in Number(10) from y in Number(20) from z in Number(10) select x * y * z, Some: x => x, None: 0, Fail: -1); So as you can see, using monadic error handling is doable in C# with the right tools. It certainly takes a bit more discipline from the programmer, but it's definitely worth it. It also makes your code declarative. A function that looks like this: TryOption<string> DoStuff(); ... clearly declares that the function may: Succeed, return 'no value', or fail. So you should deal with all 3 possibilities. In contrast, this doesn't give that information to the programmer: string DoStuff(); [1] https://github.com/louthy/language-ext
In Exceptional results: error handling in C# and Rust
That seems like a bad idea. It potentially moves the failure further away from the cause. It's nearly always better to fail fast and deal with the problem, than propagate. Obviously it'd be even better to not have null at all, or have a null that doesn't claim to honour a contract that it can't fulfil (Option/Maybe type basically). For C# programmers I attempted to help fix the problem[1] (it papers over the cracks): [1] https://github.com/louthy/language-ext
In Null Object: Something for Nothing (2003)
Of course you could do many things to make C# closer to F# (I've devoted a whole library to coercing it [1]). My preferred approach would be: var x = ... ? Foo + frob() : Baz; Or to be truer to the original (using my lib): var x = ... ? map(frob(), a => Foo + a) : Baz; But the idioms of the language mean that in the real world you see the ugly version with the associated risks of uninitialised variables. I applaud the C# team for the direction they're taking. I suspect C# will end up closer to Scala than it will to F#, which isn't a bad niche for it to be. [1] https://github.com/louthy/language-ext
In C# 7 Work List of Features

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.