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, 10:56:20 AM 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

14 sources
As a maintainer of a relatively successful open source project [1], I find I get a lot of thanks. Yes, it usually comes attached to an issue or a question, but there's not really an easier avenue to just say "thanks" either. So it doesn't bother me too much. The thank you that comes attached to issues does make me more inclined to help quicker, because I just feel the person is being polite and respectful of the effort. [1] https://github.com/louthy/language-ext
In Why we never thank open source maintainers
> Immutable data is also becoming a common pattern even in C# but it's tedious as hell to write. I can simplify it for you: public class TestClass : Record<TestClass> { public readonly int X; public readonly string Y; public readonly Guid Z; public TestClass(int x, string y, Guid z) { X = x; Y = y; Z = z; } } That is an immutable type that has structural equality and ordering, a strong GetHashCode() implementation, as well as a default ToString() which previews the members, and serialization and deserialization constructs. It's a feature I recently added to language-ext [1]. If you need `With` functionality, then add the following member: public TestClass With(int? X = null, string Y = null, Guid? Z = null) => new TestClass(X ??
Read the rest
this.X, Y ?? this.Y, Z ?? this.Z); Then you can use named parameters to do partial updates: value = value.With(X: 456, Y: "World"); Obviously it's still more boilerplate than F#, but C# is never going to have significantly less until we get proper record types and ADTs. > Functional programming is also seeping in but again tons of boiler plate. Do you have an example? It's something I've worked on quite heavily over the past few years, so I'm always looking for areas to try and improve the lot of C# devs trying to write functionally. [1] https://github.com/louthy/language-ext/releases/tag/v2.1.0
In The Problem with F# Evangelism
louthy·Aug 16, 2017Recommendation phrase
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 functional techniques into. We're (my team) finding that the pull to to F# is diminishing quite significantly; this is partly due to the functional framework features I've added to lang-ext; partly the new features of C# (expression bodied members, tuples, etc.), and majorly the relatively poor tooling. This is not some desire for ReSharper in F#, it's basic stuff like compilation speed, the amount of time it takes for the red-squiglys to turn up (lulling one into a false sense of security), build issues: I have a C#->F#->C# dependency graph in a new satellite project I'm working on. If I change the root C# project then the F# project won't re-build/link, then at runtime
Read the rest
I'll get errors that the F# DLL expected a function or type to exist that I'd removed from the C# one. And the absolute classic about the necessity for ordering files in the project in dependency order... I get that this reduces cyclomatic complexity, and that's laudable, but it's just a pain once a project gets to a reasonable size. For my open-source projects I had to drop F# APIs from the projects because I couldn't create net451 and netstandard1.X project files that would build, pack, and be deployable to nu-get. Also the interop story between C# and F# really isn't that good. Even though it's shouted from the rooftops as a main selling point. Everything prefixed with FSharp is ugly, FSharpFunc can't implicitly convert to Func, FSharpOption evaluates to null for None, so you get none of the safeness in C#, FSharpAsync is reeeaaally difficult to consume from C#, etc. This means we have to build an interop layer and that always feels like needless effort. It would be much better if there was some tooling support that could create proxies between the F# and C# worlds. It always feels like there's something in the way. And the way of human inertia is that those seemingly minor niggles will tend to put people off full-scale adoption. I love F# as a language (well, not its OO grammar, but I don't use that), but C# + language-ext actually gives me pretty much everything I need (bar ADTs). [1] https://github.com/louthy/language-ext
In The Problem with F# Evangelism
Wanted to leave a comment on the site, but it seems comments are broken, so I'll stick this here just in case the author sees it: You've linked to my csharp-monad library for C# parser combinators. This has been superseded by my language-ext project: https://github.com/louthy/language-ext/ It is a much more advanced and efficient port of the Haskell Parsec library. Would you mind linking to that instead? Your Sprache examples would look like this in language-ext: public static readonly Parser<JCLCommand> JCLText = from open in ch('$') from ws1 in spaces from command in asString(many(noneOf(' '))) from ws2 in spaces from content in asString(many(noneOf('"'))) select new JCLCommand(command, content); public static readonly Parser<JCLCommand> GlobalText = fro
Read the rest
m variablename in asString(many(noneOf('='))) from ws2 in ch('=') from openbrack in ch('(') from filepath in asString(many(noneOf(')'))) from closebrack in ch(')') select new JCLCommand(variablename, filepath); But the power of parser combinators are their reusable nature. So I'd break that down to a set of tools: static Parser<A> token<A>(Parser<A> p) => from x in p from _ in either(spaces, eof) select x; static Parser<string> symbol(string x) => token(str(x)); static Parser<string> identifier = token(asString(many1(alphaNum))); static Parser<A> quotes<A>(Parser<A> p) => between(symbol("\""), symbol("\""), p); static Parser<A> parens<A>(Parser<A> p) => between(symbol("("), symbol(")"), p); static Parser<string> quoteText = token(quotes(asString(many(satisfy(x => x != '"'))))); static Parser<string> parensText = token(parens(asString(many(satisfy(x => x != ')'))))); Then your final parsers would look like this: static readonly Parser<JCLCommand> JCLText = from open in symbol("$") from command in identifier from content in quoteText select new JCLCommand(command, content); static readonly Parser<JCLCommand> GlobalText = from variablename in identifier from ws2 in symbol("=") from filepath in parensText select new JCLCommand(variablename, filepath); static readonly Parser<Seq<JCLCommand>> Commands = from _ in spaces from commands in many1(either(JCLText, GlobalText)) select commands; Which is much easier to understand I think. It's not exactly the same as it defines what an identifier is, but it's much more tolerant of rogue spaces because of the token parser. This is definitely the most compelling aspect of parser combinators for me, the way they compose so elegantly.
In What the heck is a parser-combinator?
I'm working on it :) [1] "Also there is support for the much more difficult higher-order polymorphic types like Monad<MA, A>. LanguageExt 2.0 provides a fully type-safe and efficient approach to working with higher order types. So yes, you can now write functions that take monads, or functors, or applicatives, and return specialised types (rather than interfaces or dynamic results). So intead of writing a function that takes an option, you can write one that takes any monadic type, bind them, join them, map them, and return the concrete type that you pushed in." [1] https://github.com/louthy/language-ext/releases/tag/2.0.26-b...
In New Features in C# 7.0
I've been programming 3/4 of my life and I'd fall over on that stuff. I'm a CTO of 12 years and I'd fall over on that stuff. I have a successful open-source project [1] which is all algorithms and data-structures, and I'd fall over on that stuff. At some point interviewers must get it, that these artificial test environments bear no relevance to how we write code in the real world, and putting these false constraints on a candidate just creates stress, and in no way communicates their competence. Luckily for me (as an interviewer) I do get it. And I suspect that's part of the reason why I've not had a developer leave my company in the 12 years we've been running. I do a first interview, which I keep super chilled and chatty, just trying to get the candidate to relax. I'll focus on projects on their CV, asking high-level questions. A
Read the rest
gain, no stress. Then if they say anything interesting about a part of a project I may drill down a bit more to make sure they understand the subject they're talking about. Which is slightly more stressful, but if they're competent, they'll know. I very rarely go into algorithms and data structures. It'll usually be if I'm concerned about a junior dev not having enough CS knowledge. I then send them an email post interview (if I think I want to see them again for the second interview). The email will have a link to a partially complete project, I ask them to finish it based on a written spec. It shouldn't take more than 45mins - 1hour. The request in the spec is intentionally a bit obscure. I put at the end of the spec that "If you have any questions or problems, please email me. You won't be marked down for this." The obscureness of the test is to see who emails me to ask for help. That for me is a real positive sign. So I'm trying to create a real world test: * A spec to follow * Programming in a comfortable environment * Access to dev resource (Google/Stack Overflow/Their friends) * Access to their boss for advice And to avoid anybody trying to game it and blag their way with code that they didn't write, I quiz them about their decisions in the second interview. It works amazingly well. My dev team is something I'm super proud of. A good team of people who collaborate well and believe in the product. No hierarchy or in-fighting. It's a happy place to be. So I endorse this method :) [1] https://github.com/louthy/language-ext
In Tech Interview Torture Chamber
@LyalinDotCom, if you want a real world example of something causing poor performance, then clone my project [1]. There are a couple of 3mb T4 code-gen'd files [2][3] that bring VS to its knees. I suspect it's actually the Roslyn compiler, but can't be certain. I know that if I wipe the code in those files then the performance is restored. [1] https://github.com/louthy/language-ext/ [2] https://github.com/louthy/language-ext/blob/master/LanguageE... [3] https://github.com/louthy/language-ext/blob/master/LanguageE...
In Visual Studio 2017 Launch [video]
You can almost do this in C# now [1]. It's ad-hoc polymorphism rather than full on type-classes, but it does facilitate generic programming in a way that I've not seen used in anger before. For example, here's a definition of a super generic function that takes a functor of string and maps it to a functor of int. public static FB ParseInts<Functor, FA, FB>(FA input) where Functor : struct, Functor<FA, FB, string, int> => default(Functor).Map(input, Int32.Parse); The secret to it working is the constraint that constrains Functor generic argument to be a struct and a Functor<FA, FB, string, int>. The struct bit means I can call default(Functor) and get a valid reference back (because structs can't be null). I can then call it with a List: var list = List("100", "50", "25"); Lst<int> res1 = ParseInts<FLst<str
Read the rest
ing, int>, Lst<string>, Lst<int>>(list); Or an Option: Option<int> res2 = ParseInts<FOption<string, int>, Option<string>, Option<int>>(opt); And it will happily map the bound values. FLst and FOption are essentially the 'class instances'. Functor is the 'type class', it's just a C# interface [2]: public interface Functor<FA, FB, A, B> { FB Map(FA ma, Func<A, B> f); } The key thing is that 'this' won't be used, so the first argument is the value to be mapped. FLst looks like this [3]: public struct FLst<A, B> : Functor<Lst<A>, Lst<B>, A, B> { public Lst<B> Map(Lst<A> ma, Func<A, B> f) => ma.Map(f); } And FOption like so [4]: public struct FOption<A, B> : Functor<Option<A>, Option<B>, A, B>, BiFunctor<Option<A>, Option<B>, Unit, A, B> { public Option<B> BiMap(Option<A> ma, Func<Unit, B> fa, Func<A, B> fb) => ma.IsNone ? fa == null ? Option<B>.None : fa(unit) : fb == null ? Option<B>.None : fb(ma.Value); public Option<B> Map(Option<A> ma, Func<A, B> f) => ma.IsSome && f != null ? Optional(f(ma.Value)) : None; } As you can probably tell, the amount of clutter from specifying generic type-parameters that the compiler could (relatively) easily work out on its own, is pretty annoying. Which limits it's usefulness somewhat. But if you want to write truly generic code, it's doable (with some caveats of course. you'll notice that the Functor type isn't quite as strict say the definition in Haskell). I'm currently updating my language-ext project to add type-classes [5] and class-instances [6], in the hope that the C# team will take pity on me and make this technique a language feature (it's been seriously turning my head inside out trying to make it work with higher-order types like monads). This is already being investigated by some Roslyn team members [7]. I figured this feature was unlikely to move seriously in the near term without some indication of real world need. [1] https://github.com/louthy/language-ext/blob/type-classes/Lan... [2] https://github.com/louthy/language-ext/blob/type-classes/Lan... [3] https://github.com/louthy/language-ext/blob/type-classes/Lan... [4] https://github.com/louthy/language-ext/blob/type-classes/Lan... [5] https://github.com/louthy/language-ext/tree/type-classes/Lan... [6] https://github.com/louthy/language-ext/tree/type-classes/Lan... [7] https://github.com/MattWindsor91/roslyn/blob/master/concepts...
In The .NET Language Strategy
Author here. When I started the project C#6 was just arriving. It's awesome to see the ValueTuples coming through for C#7. I have been adding some super-powers to them for lang-ext 2.0 (which is work in progress): Example of extensions for ValueTuple<A,B,C> (there are extensions up to ValueTuple<A,B,C,D,E,F,G> and Tuple<A,B,C,D,E,F,G>) https://github.com/louthy/language-ext/blob/type-classes/Lan... It allows cool stuff like this: var abc = ('a', 'b').Add('c'); // ('a', 'b', 'c') var abcd = ('a', 'b').Add('c').Add('d'); // ('a', 'b', 'c', 'd') var abcd5 = ('a', 'b').Add('c').Add('d').Add(5); // ('a', 'b', 'c', 'd', 5) var sumA = (1, 2, 3).Sum<TInt, int>(); // 6 var sumB = (2, 4, 8).Prod
Read the rest
uct<TInt, int>(); // 64 var flag = ("one", "two", "three").Contains<TString, string>("one"); // true var str = ("Hello", " ", "World").Concat<TString, string>(); // "Hello World" var list = (List(1, 2, 3), List(4, 5, 6)).Concat<TLst<int>, Lst<int>>(); // [1,2,3,4,5,6]
In C# functional language extensions and Erlang-like concurrency system

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.