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.
Author on...
Option
, List
and Map
) to the F# equivalents, as well as interop between core BCL types and F# LanguageExt.Parsec Port of the Haskell parsec library LanguageExt.Rx Reactive Extensions support for various types within the Core LanguageExt.Sys Provides an effects wrapper around the .NET System namespace making common IO operations pure and unit-testable
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 the key functionality and bring it into scope:
global using LanguageExt; global using LanguageExt.Common; global using static LanguageExt.Prelude; global using LanguageExt.Traits; global using LanguageExt.Effects; global using LanguageExt.Pipes; global using LanguageExt.Pretty; global using LanguageExt.Traits.Domain;
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
.
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:
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:
PascalCase
in the normal waynew
. 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"));
using static LanguageExt.Prelude
are camelCase
.var x = map(opt, v => v * 2);
PascalCase
in the normal wayvar 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.
Core
Arr<A>
Immutable array Core
Seq<A>
Lazy immutable list, evaluate at-most-once - very, very fast! Core
Iterable<A>
Wrapper around IEnumerable
with support for traits - enables the higher-kinded traits to work with enumerables. Core
Lst<A>
Immutable list - use Seq
over Lst
unless you need InsertAt
Core
Map<K, V>
Immutable map Core
Map<OrdK, K, V>
Immutable map with Ord constraint on K
Core
HashMap<K, V>
Immutable hash-map Core
HashMap<EqK, K, V>
Immutable hash-map with Eq constraint on K
Core
Set<A>
Immutable set Core
Set<OrdA, A>
Immutable set with Ord constraint on A
Core
HashSet<A>
Immutable hash-set Core
HashSet<EqA, A>
Immutable hash-set with Eq constraint on A
Core
Que<A>
Immutable queue Core
Stck<A>
Immutable stack Location Feature Description Core
Option<A>
Option monad Core
OptionT<M, A>
Option monad-transformer Core
Either<L,R>
Right/Left choice monad Core
EitherT<L, M, R>
Right/Left choice monad-transformer Core
Fin<A>
Error
handling monad, like Either<Error, A>
Core
FinT<M, A>
Error
handling monad-transformer Core
Try<A>
Exception handling monad Core
TryT<M, A>
Exception handling monad-transformer Core
Validation<FAIL ,SUCCESS>
Validation applicative and monad for collecting multiple errors before aborting an operation Core
ValidationT<FAIL, M, SUCCESS>
Validation applicative and monad-transformer Location Feature Description Core
Doc<A>
Produce nicely formatted text with smart layouts Location Feature Description Core
Patch<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).
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.
Core
Applicative<F>
Applicative functor Core
Eq<A>
Ad-hoc equality trait Core
Fallible<F>
Trait that describes types that can fail Core
Foldable<T>
Aggregation over a structure Core
Functor<F>
Functor Map
Core
Has<M, TRAIT>
Used in runtimes to enable DI-like capabilities Core
Hashable<A>
Ad-hoc has-a-hash-code trait Core
Local<M, E>
Creates a local environment to run a computation Core
Monad<M>
Monad trait Core
MonadT<M, N>
Monad transformer trait Core
Monoid<A>
A monoid is a type with an identity Empty
and an associative binary operation +
Core
MonoidK<M>
Equivalent of monoids for working on higher-kinded types Core
Mutates<M, OUTER_STATE, INNER_STATE>
Used in runtimes to enable stateful operations Core
Ord<A>
Ad-hoc ordering / comparisons Core
Range<SELF, NumOrdA, A>
Abstraction of a range of values Core
Readable<M, Env>
Generalised Reader monad abstraction Core
Semigroup<A>
Provides an associative binary operation +
Core
SemigroupK<M>
Equivalent of semigroups for working with higher-kinded types Core
Stateful<M, S>
Generalised State monad abstraction Core
Traversable<T>
Traversable structures support element-wise sequencing of Applicative effects Core
Writable<M, W>
Generalised Writer monad abstraction
These work a little like type-aliasing but they impart semantic meaning and some common operators for the underlying value.
Location Feature DescriptionCore
DomainType<SELF, REPR>
Provides a mapping from SELF
to an underlying representation: REPR
Core
Identifier <SELF>
Identifiers (like IDs in databases: PersonId
for example), they are equivalent to DomaintType
with equality. Core
VectorSpace<SELF, SCALAR>
Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate. Core
Amount <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). Core
Locus <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!
For some non-reference like documentation:
v5
refactor, but should give some good insights.If you would like to get involved with this project, please first read the Contribution Guidelines and the Code of Conduct.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4