language-ext

C# functional language extensions - a base class library for functional programming

MIT License

Stars
6.2K
Committers
97

Bot releases are hidden (Show)

language-ext - Remove dependency on System.Collections.Immutable

Published by louthy about 9 years ago

Removed dependency on System.Collections.Immutable. All immutable types are implemented in-project:

Lst<T>
Map<K,V>
Set<T>
Que<T>
Stck<T>

Either and EitherUnsafe API rebalancing (between Left and Right behaviours). Also Either and EitherUnsafe are now bifunctors, bifoldable and bitraversible.

Compose and BackCompose extension methods for Func<A,B>. Also, new compose function in the Prelude

NuGet:

language-ext - Language-Ext Version 1.5 release

Published by louthy about 9 years ago

This is a major new release of Language-Ext with myriad new features and improvements. Much of this release is about refining and standardising the API. This is the first stable release of the LanguageExt.Process system (Actor system), LanguageExt.Process.FSharp (F# API for LanguageExt.Process), LanguageExt.ProcessJS (Javascript Actor system that allows seamless actor messaging from server to client), and LanguageExt.Process.Redis (for intra-system messaging and message/state persistence).

Standardisation comes in the form:

  • Making all constructor functions start with a capital letter
  • Making all fold and scan functions use the same fold function signature
  • Creating a common set of functions that all of the monad types implement

So:

  • tuple becomes Tuple
  • list becomes List
  • map becomes Map
  • cons becomes Cons
  • etc.

This makes the constructor functions consistent with Some, None, Left, Right, ... The only exception to this is the unit constructor, which stays as-is.

We also bring in a concept of a type of a 'higher kinded type' for all of the monadic types. It's not quite as effective or refined as a Haskell HKT, but it does create a standard interface (through extension methods) for all of the monadic types. The standard interface includes the following functions:

  • Sum
  • Count
  • Bind
  • Exists
  • Filter
  • Fold
  • ForAll
  • Iter
  • Map
  • Lift / LiftUnsafe
  • SelectMany
  • Select
  • Where

Because of this standardised interface, it's also possible to use monad transformer functions (for up to a two-level deep generic monad-type):

  • SumT
  • BindT
  • CountT
  • ExistsT
  • FilterT
  • FoldT
  • ForAllT
  • IterT
  • MapT

i.e.

    var list = List(Some(1), None, Some(2), None, Some(3));    // Lst<Option<int>>

    var presum = list.SumT();                                // 6

    list  = list.MapT( x => x * 2 );

    var postsum = list.SumT();                               // 12

New types

  • Map<K,V> - Replacement for ImmutableDictionary<K,V> that uses an AVL tree implementation
  • Lst<T> - Wrapper for ImmutableList<T>
  • Set<T> - Wrapper for ImmutableSet<T>
  • Try<T> - Like TryOption<T> but without the optional return value
  • RWS<E,W,S> - Reader/Writer/State monad
  • ExceptionMatch - Used for new Exception extension method Match for pattern matching on exception types.
  • ActionObservable<T> - Used by IObservable<T> extension method PostSubscribe. Allows an action to be executed post-subscription.

New functions

  • static Process functions in LanguageExt.Process
  • par - Partial application
  • curry - Currying
  • random - Thread safe random number generator
  • ifSome added for TryOption - dispatches an action if the TryOption is in a Some state

Improvements

  • Improved Unit type - Implemented IEquatable<Unit> and equality operator overloads
  • Improved IComparable and IEquatable support for Option and Either types

Breaking changes

  • Standardised the fold and scan delegates to Func<S,T,S>

Deprecated (made obsolete)

  • tuple - becomes Tuple
  • query - becomes Query
  • map - becomes Map
  • list - becomes List
  • array - becomes Array
  • stack - becomes Stack
  • failure - becomes ifNone / ifLeft
  • Prelude.empty - becomes List.empty / Set.empty / Map.empty
  • cons - becomes Cons
  • range - becomes Range
  • with - becomes map

Nuget:

language-ext - Language-ext 1.0.0

Published by louthy over 9 years ago

Version 1.0.0

Now that System.Collections.Immutable has been fully released (as version 1.1.36) we can now do a full release of Language-Ext.

As usual you can find the package on NuGet

Language-ext

Use and abuse the features of C#, which, if you squint, can look like extensions to the language itself. This package is a functional 'toolkit' and also solves some of the annoyances with C#, namely:

  • Poor tuple support
  • Null reference problem
  • Lack of lambda and expression inference
  • Void isn't a real type
  • Mutable lists, dictionaries, sets, queues, etc.
  • The awful 'out' parameter
  • Common core functional types missing (Option, Either, Unit, ...)

The library very heavily focusses on correctness, to give you the tools needed to write safe declarative code.

Features:

Powerful 'prelude' which you include by using static LanguageExt.Prelude (in C# 6) that covers many of the basic functional language core library functions and types (from using LanguageExt):

  • Pattern matching
  • Lambda type-inference: var fn = fun( (int x, int y) => x + y );
  • Option<T>, OptionUnsafe<T>, Either<L,R>, EitherUnsafe<L,R> and TryOption<T> monads (probably the most complete implementations you'll find in the .NET world)
  • tuple(a,b,...) - Tuple construction without typing Tuple.Create(a,b,...) as well as map to project the Item1..ItemN properties onto named values.
  • List - immutable list
  • Map - immutable map
  • Set - immutable set
  • memo - Memoization with auto-cache purging using weak-references
  • Writer monad
  • Reader monad
  • State monad
  • Extension methods and replacement functions for dealing with out (Int32.TryParse, IDictionary.TryGetValue, etc.)

This only skims the surface of the library

Release notes

Additions:

  • New Reader<E,T> monad
    • Prelude.Reader constructor function
    • Prelude.ask function
    • Prelude.local function
  • New Writer<W,T> monad
    • Prelude.Writer constructor function
    • Prelude.tell function
  • New State<S,T> monad
    • Prelude.State constructor function
    • Prelude.get function
    • Prelude.put function
  • Option<T>
    • IfSome method for dispatching actions and ignoring None
    • Prelude.ifSome as above
    • IfNone method (replaces Failure)
    • Prelude.ifNone function (replaces Prelude.failure)
    • ToEither converts an Option<T> to an Either<L,R> (you must provide a default(L) value or func incase the Option is in a None state)
    • ToEitherUnsafe converts an Option<T> to an EitherUnsafe<L,R> (you must provide a default(L) value or func incase the Option is in a None state)
    • Some fluent method now also supports Action
      *OptionUnsafe<T>
    • IfSomeUnsafe method for dispatching actions and ignoring None
    • Prelude.ifSomeUnsafe as above
    • IfNoneUnsafe method (replaces FailureUnsafe)
    • Prelude.ifNoneUnsafe function (replaces Prelude.failureUnsafe)
    • ToEitherUnsafe converts an OptionUnsafe<T> to an EitherUnsafe<L,R> (you must provide a default(L) value or func incase the OptionUnsafe is in a None state)
    • Some fluent method now also supports Action
  • TryOption<T>
    • IfSome method for dispatching actions and ignoring None or Fail
    • Prelude.ifSome as above
    • IfNone method (replaces Failure)
    • Prelude.ifNone function (replaces Prelude.failure)
    • IfNoneOrFail method for handling both failure states separately (Some state uses identity function)
    • Prelude.ifNoneOrFail as above
    • TryOptionConfig.ErrorLogger static variable which can be used to attach error logging behaviour to the Fail state of TryOption
    • Prelude.tryfun function wraps a TryOption in a Func
    • ToOption converts a TryOption<T> to a Option<T> (Fail becomes None)
    • Some fluent method now also supports Action
  • Either<L,R>
    • IfRight method for dispatching actions and ignoring Left
    • Prelude.ifRight as above
    • IfLeft method (replaces Failure)
    • Prelude.ifLeft method (replaces Prelude.failure)
    • Right fluent method now also supports Action
    • ToOption converts an Either&lt;L,R&gt; to an Option&lt;R&gt; (L becomes None)
    • ToTryOption converts an Either&lt;L,R&gt; to a TryOption&lt;R&gt;
    • ToEitherUnsafe converts an Either&lt;L,R&gt; to an EitherUnsafe&lt;L,R&gt; (L becomes None)
  • EitherUnsafe<L,R>
    • IfRightUnsafe method for dispatching actions and ignoring Left
    • Prelude.ifRightUnsafe as above
    • IfLeftUnsafe method (replaces FailureUnsafe)
    • Prelude.ifLeftUnsafe method (replaces Prelude.failureUnsafe)
    • Right fluent method now also supports Action

Updates:

  • Prelude.convert<T> now returns None if input is null (it previously threw an exception)

Fixes:

  • Query.zip would go into an infinite loop. Fixed.
  • Comments

Deprecated:

  • Dependency on ConcurrentHashTable
  • failure and Failure (for ifNone, IfNone, ifLeft, etc.)
  • Iter extension method in Query, it was causing resolution problems for the compiler.
  • Removed RightUnsafe and LeftUnsafe from Either, these were a hangover from when EitherUnsafe didn't exist and Either had a dual role. This isn't needed any more.

https://www.nuget.org/packages/LanguageExt/

language-ext - Queryable and TypeConvertor support

Published by louthy over 9 years ago

Deprecated headSafe
Added headOrNone for safely finding the optional head item in a collection
Added Query namespace (static class) for working with IQueryable. Has functions:

  • headOrNone
  • tail
  • map
  • filter
  • choose
  • collect
  • sum
  • rev
  • append
  • fold
  • foldBack
  • reduce
  • reduceBack
  • find
  • freeze
  • zip
  • length
  • iter
  • forall
  • distinct
  • take
  • takeWhile
  • exists
    Option and Some TypeConverter support
language-ext - Either RL to LR flip - Breaking change!

Published by louthy over 9 years ago

    Either<R,L>

becomes:

    Either<L,R>

Warning: if you're currently using Either then getting the latest will break your compilation. You will need to update all usages of Either<R,L> to Either<L,R>, which is potentially tedious for large projects. Please be warned.

Normally I wouldn't do such a big breaking change. But I have finally been convinced that this is the correct way and I think it's best to just bite the bullet and get it done. Apologies if this causes headaches, I promise it won't happen again!

Latest on nuget: https://www.nuget.org/packages/LanguageExt/

language-ext - API refinement and bug fixes

Published by louthy over 9 years ago

Operators

None and Left coalescing using the logical-or operator.

This means chaining of optional values with defaults:

   Option<int> optional1 = None;
   Option<int> optional2 = None;

   var res = optional1 || optional2 || "Default";  
   // Some("Default")

This also means you can use them in LINQ expressions where from is logical-AND and || is logical-OR:

    Option<int> optional1 = None;
    Option<int> optional2 = Some(10);
    Option<int> optional3 = Some(20);

    var res = from x in optional1 || optional2
              from y in optional3
              select x + y;

    // Some(30)

true and false operators implemented for option and either types.

    var optional = Some(123);
    if( optional )
    {
       // true
    }

== and != operators implemented for option and either types:

    var optional = Some(123);
    if( optional == 123 )
    {
       // true
    }

    Option<int> optional = None;
    if( optional == None )
    {
       // true
    }

With

with(Tuple,..) and With(Tuple,..) are now map(Tuple,..) and Map(Tuple,..). They were poorly named, these are more consistent with their action. The with functions that take up to 5 parameters are also renamed map.

The old with functions have been marked [Obsolete]

Thanks to @tomaszpolanski for the feedback
https://github.com/louthy/language-ext/issues/15

Optional

Added Optional, it will take T or null and return a Some(T) or None respectively. This is for where you want to be explicit about the conversion from T to Some(T)|None, and for when the type system complains at you (in the branches of a ternary operator for example):

   string x = GetValueFromNonTrustedAPI();
   return Optional(x);

Thanks to @tejacques for the feedback:
https://github.com/louthy/language-ext/issues/12

... and Various bug fixes

NuGet: https://www.nuget.org/packages/LanguageExt/

language-ext - More list matching, Collections functions (List, Map, Set), Range improvements

Published by louthy almost 10 years ago

List matching

Improved list matching. There are many more overrides for deconstructing the head items from a list (up to six elements). e.g.

    // The fewest number of elements deconstructed
    int Product(IEnumerable<int> list) =>
        match(list,
            ()      => 1,
            (x, xs) => x * Product(xs));

    // The most
    int GetLength(IEnumerable<int> list) =>
        match(
            lst,
            () => 0,
            a => 1,
            (a, b) => 2,
            (a, b, c) => 3,
            (a, b, c, d) => 4,
            (a, b, c, d, e) => 5,
            (a, b, c, d, e, f) => 6,
            (x, xs) => xs.Count() + 1
        );

Also, a null list is considered empty.

range

Additional range() variants:

  • Ranges with different step sizes - range(0,100,10)
  • range of ranges - range( range(0,100), range(200,100) )
  • range of chars - range('a','z')

List

Additional functions:

  • repeat
  • init - generate a sequence where each step calls a generator function with the index
  • initInfinite
  • choose
  • collect
  • scan
  • scanBack
  • find
  • distinct
  • take
  • takeWhile
  • unfold
  • exists

Breaking changes

  • foldr renamed foldBack
  • Removed the variants of map and iter (mapi and iteri) and just used overloading instead
  • Fluent variants now use Pascal Case naming

Map

Additional function:

  • exists

Breaking changes

  • contains renamed containsKey
  • Fluent variants now use Pascal Case naming

Set

Additional functions:

  • add
  • compare
  • length
  • difference
  • exists
  • filter
  • intersect
  • map
  • contains
  • remove
  • isSubset
  • isProperSubset

Tuple

Breaking change

  • Removed this from with, they shouldn't be extension methods because With is already performing that duty.

Assorted updates

  • convert returns Option<T>
  • Added TryGetValue for ImmutableDictionary and ImmutableSet
  • Added AsEnumerable() extension to Nullable<T>
  • Functions for converting IEnumerable to immutable collections: toList, toArray, toSet, toQueue, toStack

On NuGet now: https://www.nuget.org/packages/LanguageExt/

language-ext - List pattern matching

Published by louthy almost 10 years ago

List pattern matching:

        public int Sum(IEnumerable<int> list) =>
            match( list,
                   ()      => 0,
                   x       => x,
                   (x, xs) => x + Sum(xs) );

Extension methods for lifting Option, OptionUnsafe, Either, EitherUnsafe when wrapped by an IEnumerable.

TryOption<T>.AsEnumerable() now returns an IEnumerable<Either<T,Exception>> instead of burying the Exception as an empty list. The extension methods above can be used to easily extract the value from the IEnumerable<Either<T,Exception>>. This puts control back in the programmer's hands about what to do with the possible failed state of the TryOption.

e.g.

            // Ignore all errors, and return an empty list
            var res = (from v in GetTryOptionError().AsEnumerable().FailWithEmpty()
                       from r in range(1, 10)
                       select v * r);

            // Ignore the errors and provide a sensible default
            var res = (from v in GetTryOptionValue(false).AsEnumerable().Failure( list(1) )
                       from r in range(1, 10)
                       select v * r);

            // Recognise the error, and throw
            var res = (from v in match( 
                                     GetTryOptionValue(true).AsEnumerable(), 
                                     Right: r => list(r),
                                     Left:  ex => failwith<int>("broken: " + ex.Message)
                                 )
                       from r in range(1, 10)
                       select v * r);

Check LinqTests.cs and ListMatchingTests.cs for examples.

language-ext - New functions and memoization improvements

Published by louthy almost 10 years ago

New functions for Option, OptionUnsafe, TryOption:

  • bind
  • map
  • exists
  • fold
  • count
  • forall
  • ToList
  • ToArray

New functions in List and Map:

  • iter
  • forall
  • mapi
  • addRange

Improved memoization:

  • Uses a WeakDictionary from the ch.codeplex.com library, this removes the possibility of memory-leaks whilst maintaining the compositional nature of Func<T,R>. In a unit-test memoizing 0 -> Int32.MaxValue strings, the NUnit runner never jumped above 45mb.
  • memo() must use a reference type for R, so the old memoization function is kept for completeness, except it's now called memoUnsafe, with warnings in the comments about potential memory leaks.

Breaking changes:

  • each() has been renamed iter()
  • AsEnumerableOne() renamed AsEnumerable() and removed the infinite variants (originally called AsEnumerable())

https://www.nuget.org/packages/LanguageExt/

language-ext - TryOption and standalone 'Unsafe' variants

Published by louthy almost 10 years ago

New TryOption<T> type for catching all three possible outputs of a function: value, null, Exception. With three branches in Match: Some|None|Fail.

Option<T> and Either<R,L> have been split in to Option<T>, OptionUnsafe<T>, Either<R,L> and EitherUnsafe<R,L>. The 'unsafe' variants are for the cases where null is actually a valid Some value. They are only 'unsafe' because the of this reason.

Construction of OptionUnsafe<T> and EitherUnsafe<R,L> is done using SomeUnsafe(value), RightUnsafe(value), LeftUnsafe(value). Matching is done using matchUnsafe(...) and failureUnsafe.

This is much more declarative, and will clearly bias the programmer toward the safe types, only opting out when they absolutely need null as a valid value.

Breaking change: set becomes setItem.

New prelude functions Description
stack<T>() Create an IImmutableStack
set<T>() Create an IImmutableHashSet
array<T>() Create an ImmutableArray
queue<T>() Create an IImmutableQueue
New List functions Description
freeze Takes an IEnumerable and turns it into an IImmutableList
zip Wrapper for Enumerable.Zip
length Wrapper for IImmutableList.Count()
New Map functions Description
length Wrapper for IImmutableDictionary.Count()

https://www.nuget.org/packages/LanguageExt/

language-ext - First batch of feedback release

Published by louthy almost 10 years ago

Option and Either updates

  • Some(null) now doesn't coerce to a None, a ValueIsNullException will be thrown
  • Some(Nullable<T>) supported, will coerce the Value to Some if HasValue is true, otherwise a ValueIsNullException will be thrown
  • Right(Nullable<T>)/Left(Nullable<T>) supported, will coerce the Value to Right/Left if HasValue is true, otherwise a ValueIsNullException will be thrown
  • SomeUnsafe() added : Allows Some(null) and allows the Some and None branches of match to return null.
  • RightUnsafe()/LeftUnsafe() added : Allows Right(null) and Left(null) and allows the Right and Left branches of match to return null.
  • Added IsUnsafe to Option<T> and Either<R,L>

I'm considering whether the 'unsafe' variants should return a OptionUnsafe<T> and EitherUnsafe<R,L> to make it explicit that the Some,Right and Left branches could have null and that's a valid result. I'm inviting feedback...

Package Rankings
Top 3.9% on Proxy.golang.org
Badges
Extracted from project README
GitHub Discussions