louthy/language-ext

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

C#

7,084

3,061 commits

updated Jul 29, 2026

See the code
ad-hoc-polymorphism
applicative
bcl
c-sharp
f-sharp
functional-languages
functional-programming
functor
higher-kinded-types
immutable-collections
immutable-types
language-ext
lenses
monad
monads
monad-transformers
monoid
records
semigroup
structural-equality

README

lang-ext

C# Functional Programming Language Extensions

This library uses and abuses the features of C# to provide a pure functional-programming framework that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's and team's cognitive load.

GitHub Discussions

Author on...

Contents

Reference

Nu-get

Nu-get packageDescription
LanguageExt.ParsecPort of the Haskell parsec library
LanguageExt.StreamingA set of compositional streaming types
LanguageExt.FSharpF# to C# interop package. Provides interop between the LanguageExt.Core types (like Option, List and Map) to the F# equivalents, as well as interop between core BCL types and F#
LanguageExt.ParsecPort of the Haskell parsec library
LanguageExt.RxReactive Extensions support for various types within the Core
LanguageExt.SysProvides an effects wrapper around the .NET System namespace making common IO operations pure and unit-testable

lang-ext

Getting started

To use this library, simply include LanguageExt.Core.dll in your project or grab it from NuGet. It is also worth setting up some global using for your project. This is the full list that will cover all functionality and bring it into scope:

global using LanguageExt;
global using LanguageExt.Common;
global using LanguageExt.Traits;
global using LanguageExt.Effects;
global using LanguageExt.Streaming;
global using LanguageExt.Pretty;
global using LanguageExt.Traits.Domain;
global using static LanguageExt.Prelude;

A minimum, might be:

global using LanguageExt;
global using static LanguageExt.Prelude;

The namespace LanguageExt contains most of the core types; LanguageExt.Prelude contains the functions that bring into scope the prelude functions that behave like standalone functions in ML style functional programming languages; LanguageExt.Traits brings in the higher-kinded trait-types and many extensions; LanguageExt.Common brings in the Error type and predefined Errors.

Prologue

From C# 6 onwards we got the ability to treat static classes like namespaces. This means that we can use static methods without qualifying them first. That instantly gives us access to single term method names that look exactly like functions in ML-style functional languages. i.e.

    using static System.Console;
    
    WriteLine("Hello, World");

This library tries to bring some of the functional world into C#. It won't always sit well with the seasoned C# OO programmer, especially the choice of camelCase names for a lot of functions and the seeming 'globalness' of a lot of the library.

I can understand that much of this library is non-idiomatic, but when you think of the journey C# has been on, is "idiomatic" necessarily right? A lot of C#'s idioms are inherited from Java and C# 1.0. Since then we've had generics, closures, Func, LINQ, async... C# as a language is becoming more and more like a functional language on every release. In fact, the bulk of the new features are either inspired by or directly taken from features in functional languages. So perhaps it's time to move the C# idioms closer to the functional world's idioms?

My goal with this library is very much to create a whole new community within the larger C# community. This community is not constrained by the dogma of the past or by the norms of C#. It understands that the OOP approach to programming has some problems and tries to address them head-on.

And for those that say "just use F#" or "just use Haskell", sure, go do that. But it's important to remember that C# has a lot going for it:

  • Incredible investment into a state-of-the art compiler
  • Incredible tooling (Visual Studio and Rider)
  • A large ecosystem of open-source libraries
  • A large community of developers already using it
    • This is also very important for companies that hire engineers
  • It is a functional programming language! It has first-class functions, lambdas, etc.
    • And with this library it has a functional-first Base Class Library

A note about naming

One of the areas that's likely to get seasoned C# heads worked up is my choice of naming style. The intent is to try and make something that feels like a functional language rather than following rules of naming conventions (mostly set out by the BCL).

There is, however, a naming guide that will keep you in good stead while reading through this documentation:

  • Type names are PascalCase in the normal way
  • The types all have constructor functions rather than public constructors that you instantiate with new. They will always be PascalCase:
    Option<int> x = Some(123);
    Option<int> y = None;
    Seq<int> items = Seq(1,2,3,4,5);
    List<int> items = List(1,2,3,4,5);
    HashMap<int, string> dict = HashMap((1, "Hello"), (2, "World"));
    Map<int, string> dict = Map((1, "Hello"), (2, "World"));
  • Any (non-type constructor) static function that can be used on its own by using static LanguageExt.Prelude are camelCase.
    var x = map(opt, v => v * 2);
  • Any extension methods, or anything "fluent" are PascalCase in the normal way
    var x = opt.Map(v => v * 2);

Even if you disagree with this non-idiomatic approach, all of the camelCase static functions have fluent variants, so you never actually have to see the non-standard stuff.

Features

Functional effects and IO

Atomic concurrency and collections

Immutable collections

LocationFeatureDescription
CoreArr<A>Immutable array
CoreSeq<A>Lazy immutable list, evaluate at-most-once - very, very fast!
CoreIterable<A>Wrapper around IEnumerable with support for traits - enables the higher-kinded traits to work with enumerables.
CoreLst<A>Immutable list - use Seq over Lst unless you need InsertAt
CoreMap<K, V>Immutable map
CoreMap<OrdK, K, V>Immutable map with Ord constraint on K
CoreHashMap<K, V>Immutable hash-map
CoreHashMap<EqK, K, V>Immutable hash-map with Eq constraint on K
CoreSet<A>Immutable set
CoreSet<OrdA, A>Immutable set with Ord constraint on A
CoreHashSet<A>Immutable hash-set
CoreHashSet<EqA, A>Immutable hash-set with Eq constraint on A
CoreQue<A>Immutable queue
CoreStck<A>Immutable stack

Functional streams

Optional and alternative value monads

LocationFeatureDescription
CoreOption<A>Option monad
CoreOptionT<M, A>Option monad-transformer
CoreEither<L,R>Right/Left choice monad
CoreEitherT<L, M, R>Right/Left choice monad-transformer
CoreFin<A>Error handling monad, like Either<Error, A>
CoreFinT<M, A>Error handling monad-transformer
CoreTry<A>Exception handling monad
CoreTryT<M, A>Exception handling monad-transformer
CoreValidation<FAIL ,SUCCESS>Validation applicative and monad for collecting multiple errors before aborting an operation
CoreValidationT<FAIL, M, SUCCESS>Validation applicative and monad-transformer

State managing monads

LocationFeatureDescription
CoreReader<E, A>Reader monad
CoreReaderT<E, M, A>Reader monad-transformer
CoreWriter<W, A>Writer monad that logs to a W constrained to be a Monoid
CoreWriterT<W, M, A>Writer monad-transformer
CoreState<S, A>State monad
CoreStateT<S, M, A>State monad-transformer

Parser combinators

Pretty

LocationFeatureDescription
CoreDoc<A>Produce nicely formatted text with smart layouts

Differencing

LocationFeatureDescription
CorePatch<EqA, A>Uses patch-theory to efficiently calculate the difference (Patch.diff(list1, list2)) between two collections of A and build a patch which can be applied (Patch.apply(patch, list)) to one to make the other (think git diff).

Traits

The traits are major feature of v5+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's series on Higher Kinds to get a deeper insight.

Value traits

These work a little like type-aliasing but they impart semantic meaning and some common operators for the underlying value.

LocationFeatureDescription
CoreDomainType<SELF, REPR>Provides a mapping from SELF to an underlying representation: REPR
CoreIdentifier <SELF>Identifiers (like IDs in databases: PersonId for example), they are equivalent to DomaintType with equality.
CoreVectorSpace<SELF, SCALAR>Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate.
CoreAmount <SELF, SCALAR>Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives VectorSpace, IdentifierLike, DomainType, and is orderable (comparable).
CoreLocus <SELF, DISTANCE, SCALAR>Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives DomainType, IdentifierLike, AmountLike and VectorSpace. DISTANCE must also be an AmountLike<SELF, REPR, SCALAR>.

These features are still a little in-flux as of 17th Oct 2024 - they may evolve, be renamed, or removed - but I like the idea!

Further

For some non-reference like documentation:

  • Paul's blog: Notes from a Small Functional Island does deep dives into the philosophy of FP and the inner-workings of language-ext.
  • The wiki has some additional documentation, some might be a little out of date since the big v5 refactor, but should give some good insights.

Contributing & Code of Conduct

If you would like to get involved with this project, please first read the Contribution Guidelines and the Code of Conduct.

Contributors

(top 30 of 82)

louthy

2,606 commits

TysonMN

103 commits

blakeSaucier

82 commits

StefanBertels

51 commits

louthy/language-ext

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

C#

7,084

3,061 commits

updated Jul 29, 2026

See the code
ad-hoc-polymorphism
applicative
bcl
c-sharp
f-sharp
functional-languages
functional-programming
functor
higher-kinded-types
immutable-collections
immutable-types
language-ext
lenses
monad
monads
monad-transformers
monoid
records
semigroup
structural-equality

README

lang-ext

C# Functional Programming Language Extensions

This library uses and abuses the features of C# to provide a pure functional-programming framework that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's and team's cognitive load.

GitHub Discussions

Author on...

Contents

Reference

Nu-get

Nu-get packageDescription
LanguageExt.ParsecPort of the Haskell parsec library
LanguageExt.StreamingA set of compositional streaming types
LanguageExt.FSharpF# to C# interop package. Provides interop between the LanguageExt.Core types (like Option, List and Map) to the F# equivalents, as well as interop between core BCL types and F#
LanguageExt.ParsecPort of the Haskell parsec library
LanguageExt.RxReactive Extensions support for various types within the Core
LanguageExt.SysProvides an effects wrapper around the .NET System namespace making common IO operations pure and unit-testable

lang-ext

Getting started

To use this library, simply include LanguageExt.Core.dll in your project or grab it from NuGet. It is also worth setting up some global using for your project. This is the full list that will cover all functionality and bring it into scope:

global using LanguageExt;
global using LanguageExt.Common;
global using LanguageExt.Traits;
global using LanguageExt.Effects;
global using LanguageExt.Streaming;
global using LanguageExt.Pretty;
global using LanguageExt.Traits.Domain;
global using static LanguageExt.Prelude;

A minimum, might be:

global using LanguageExt;
global using static LanguageExt.Prelude;

The namespace LanguageExt contains most of the core types; LanguageExt.Prelude contains the functions that bring into scope the prelude functions that behave like standalone functions in ML style functional programming languages; LanguageExt.Traits brings in the higher-kinded trait-types and many extensions; LanguageExt.Common brings in the Error type and predefined Errors.

Prologue

From C# 6 onwards we got the ability to treat static classes like namespaces. This means that we can use static methods without qualifying them first. That instantly gives us access to single term method names that look exactly like functions in ML-style functional languages. i.e.

    using static System.Console;
    
    WriteLine("Hello, World");

This library tries to bring some of the functional world into C#. It won't always sit well with the seasoned C# OO programmer, especially the choice of camelCase names for a lot of functions and the seeming 'globalness' of a lot of the library.

I can understand that much of this library is non-idiomatic, but when you think of the journey C# has been on, is "idiomatic" necessarily right? A lot of C#'s idioms are inherited from Java and C# 1.0. Since then we've had generics, closures, Func, LINQ, async... C# as a language is becoming more and more like a functional language on every release. In fact, the bulk of the new features are either inspired by or directly taken from features in functional languages. So perhaps it's time to move the C# idioms closer to the functional world's idioms?

My goal with this library is very much to create a whole new community within the larger C# community. This community is not constrained by the dogma of the past or by the norms of C#. It understands that the OOP approach to programming has some problems and tries to address them head-on.

And for those that say "just use F#" or "just use Haskell", sure, go do that. But it's important to remember that C# has a lot going for it:

  • Incredible investment into a state-of-the art compiler
  • Incredible tooling (Visual Studio and Rider)
  • A large ecosystem of open-source libraries
  • A large community of developers already using it
    • This is also very important for companies that hire engineers
  • It is a functional programming language! It has first-class functions, lambdas, etc.
    • And with this library it has a functional-first Base Class Library

A note about naming

One of the areas that's likely to get seasoned C# heads worked up is my choice of naming style. The intent is to try and make something that feels like a functional language rather than following rules of naming conventions (mostly set out by the BCL).

There is, however, a naming guide that will keep you in good stead while reading through this documentation:

  • Type names are PascalCase in the normal way
  • The types all have constructor functions rather than public constructors that you instantiate with new. They will always be PascalCase:
    Option<int> x = Some(123);
    Option<int> y = None;
    Seq<int> items = Seq(1,2,3,4,5);
    List<int> items = List(1,2,3,4,5);
    HashMap<int, string> dict = HashMap((1, "Hello"), (2, "World"));
    Map<int, string> dict = Map((1, "Hello"), (2, "World"));
  • Any (non-type constructor) static function that can be used on its own by using static LanguageExt.Prelude are camelCase.
    var x = map(opt, v => v * 2);
  • Any extension methods, or anything "fluent" are PascalCase in the normal way
    var x = opt.Map(v => v * 2);

Even if you disagree with this non-idiomatic approach, all of the camelCase static functions have fluent variants, so you never actually have to see the non-standard stuff.

Features

Functional effects and IO

Atomic concurrency and collections

Immutable collections

LocationFeatureDescription
CoreArr<A>Immutable array
CoreSeq<A>Lazy immutable list, evaluate at-most-once - very, very fast!
CoreIterable<A>Wrapper around IEnumerable with support for traits - enables the higher-kinded traits to work with enumerables.
CoreLst<A>Immutable list - use Seq over Lst unless you need InsertAt
CoreMap<K, V>Immutable map
CoreMap<OrdK, K, V>Immutable map with Ord constraint on K
CoreHashMap<K, V>Immutable hash-map
CoreHashMap<EqK, K, V>Immutable hash-map with Eq constraint on K
CoreSet<A>Immutable set
CoreSet<OrdA, A>Immutable set with Ord constraint on A
CoreHashSet<A>Immutable hash-set
CoreHashSet<EqA, A>Immutable hash-set with Eq constraint on A
CoreQue<A>Immutable queue
CoreStck<A>Immutable stack

Functional streams

Optional and alternative value monads

LocationFeatureDescription
CoreOption<A>Option monad
CoreOptionT<M, A>Option monad-transformer
CoreEither<L,R>Right/Left choice monad
CoreEitherT<L, M, R>Right/Left choice monad-transformer
CoreFin<A>Error handling monad, like Either<Error, A>
CoreFinT<M, A>Error handling monad-transformer
CoreTry<A>Exception handling monad
CoreTryT<M, A>Exception handling monad-transformer
CoreValidation<FAIL ,SUCCESS>Validation applicative and monad for collecting multiple errors before aborting an operation
CoreValidationT<FAIL, M, SUCCESS>Validation applicative and monad-transformer

State managing monads

LocationFeatureDescription
CoreReader<E, A>Reader monad
CoreReaderT<E, M, A>Reader monad-transformer
CoreWriter<W, A>Writer monad that logs to a W constrained to be a Monoid
CoreWriterT<W, M, A>Writer monad-transformer
CoreState<S, A>State monad
CoreStateT<S, M, A>State monad-transformer

Parser combinators

Pretty

LocationFeatureDescription
CoreDoc<A>Produce nicely formatted text with smart layouts

Differencing

LocationFeatureDescription
CorePatch<EqA, A>Uses patch-theory to efficiently calculate the difference (Patch.diff(list1, list2)) between two collections of A and build a patch which can be applied (Patch.apply(patch, list)) to one to make the other (think git diff).

Traits

The traits are major feature of v5+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's series on Higher Kinds to get a deeper insight.

Value traits

These work a little like type-aliasing but they impart semantic meaning and some common operators for the underlying value.

LocationFeatureDescription
CoreDomainType<SELF, REPR>Provides a mapping from SELF to an underlying representation: REPR
CoreIdentifier <SELF>Identifiers (like IDs in databases: PersonId for example), they are equivalent to DomaintType with equality.
CoreVectorSpace<SELF, SCALAR>Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate.
CoreAmount <SELF, SCALAR>Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives VectorSpace, IdentifierLike, DomainType, and is orderable (comparable).
CoreLocus <SELF, DISTANCE, SCALAR>Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives DomainType, IdentifierLike, AmountLike and VectorSpace. DISTANCE must also be an AmountLike<SELF, REPR, SCALAR>.

These features are still a little in-flux as of 17th Oct 2024 - they may evolve, be renamed, or removed - but I like the idea!

Further

For some non-reference like documentation:

  • Paul's blog: Notes from a Small Functional Island does deep dives into the philosophy of FP and the inner-workings of language-ext.
  • The wiki has some additional documentation, some might be a little out of date since the big v5 refactor, but should give some good insights.

Contributing & Code of Conduct

If you would like to get involved with this project, please first read the Contribution Guidelines and the Code of Conduct.

See what people are saying

Contributors

(top 30 of 82)

louthy

2,606 commits

TysonMN

103 commits

blakeSaucier

82 commits

StefanBertels

51 commits

Languages

C#

99.9%