All notable changes to this project will be documented in this file. Funcky adheres to Semantic Versioning.
This update is mainly to update to .NET 8 but also has several smaller improvements.
Both Funcky and Funcky.Async have been annotated to be compatible with Native AOT.
The only exception is OptionJsonSerializer
which is not compatible with Native AOT.
We use the new C#12 and .NET features in the code, and expose new features through our API.
- .NET 8 added new overloads to their
TryParse
APIs. These changes are reflected in Funcky'sParseOrNone
APIs.ParseByteOrNone
overloads withReadOnlySpan<byte>
andstring?
ParseSByteOrNone
overloads withReadOnlySpan<byte>
ParseSingleOrNone
overloads withReadOnlySpan<byte>
ParseDoubleOrNone
overloads withReadOnlySpan<byte>
ParseDecimalOrNone
overloads withReadOnlySpan<byte>
ParseInt16OrNone
overloads withReadOnlySpan<byte>
ParseInt32OrNone
overloads withReadOnlySpan<byte>
ParseInt64OrNone
overloads withReadOnlySpan<byte>
ParseUInt16OrNone
overloads withReadOnlySpan<byte>
ParseUInt32OrNone
overloads withReadOnlySpan<byte>
ParseUInt64OrNone
overloads withReadOnlySpan<byte>
ParseNumberOrNone<TNumber>
overloadsParseOrNone<TParsable>
overloads
We implemented a few of the IEnumerable extensions which are very useful on strings.
Chunk
onstring
.SlidingWindow
onstring
.
- Implemented
UpCast
for the mondsOption
,Either
,Result
andSystem.Lazy
. - Implemented
InspectEmpty
onIEnumerable
andIAsyncEnumerable
- Implemented
ToAsyncEnumerable
extension onOption
MoveNextOrNone
extension onIEnumerator<T>
FindIndexOrNone
andFindLastIndexOrNone
extensions onList
This is a relatively minor release focuses on convenience for our monads Option
, Either
and Result
.
We've added GetOrElse
and OrElse
to Either
and Result
bringing them on par with Option
.
The corresponding analyzer now also correctly suggests using these methods instead of Match
for Result
and Either
.
All three alternative monads Option
, Either
and Result
now support inspecting the «error» case:
Option.InspectNone
- executes a side effect only when the option isNone
.Either.InspectLeft
- executes a side effect only when the either is on theLeft
side.Result.InspectError
- executes a side effect only when the result is anError
.
These methods are particularly useful for logging warnings/errors.
- Funcky.XUnit is only compatible with XUnit 2.4, this is now correctly declared.
We've added support for C# 11's List Patterns to Option<T>
.
This means that you can use regular switch
expressions / statements to match on options:
var greeting = person switch
{
{ FirstName: var firstName, LastName: [var lastName] } => $"Hello {firstName} {lastName}",
{ FirstName: var firstName } => $"Hi {firstName}",
};
record Person(string FirstName, Option<string> LastName);
The new Discard.__
field provides a short-hand for Unit.Value
to be used with switch
expressions.
using static Funcky.Discard;
return __ switch
{
_ when user.IsFrenchAdmin() => "le sécret",
_ when user.IsAdmin() => "secret",
_ => "(redacted)",
};
We've added overloads to the Retry
and RetryAsync
functions that allow retrying a function
as long as an exception is thrown.
Example from IoRetrier
:
// Retries an action until the file is no longer in use.
Task RetryWhileFileIsInUseAsync(IRetryPolicy policy, Action action)
=> RetryAsync(ActionToUnit(action), exception => exception is IOException && exception.HResult == FileInUseHResult, policy);
EnumerableExtensions.MinByOrNone
EnumerableExtensions.MaxByOrNone
ImmutableListExtensions.IndexOfOrNone
ImmutableListExtensions.LastIndexOfOrNone
ListExtensions.IndexOfOrNone
- ✨
OptionExtensions.ToNullable
✨ StreamExtenions.ReadByteOrNone
- New overloads for
ElementAtOrNone
that take anIndex
. - New overload for
JoinToString
that takes anIEnumerable<string>
.
- .NET 7 added new overloads to their
TryParse
APIs. These changes are reflected in Funcky'sParseOrNone
APIs. - The
ParseOrNone
methods include the new[StringSyntax]
attribute from .NET 7.
The new Option.Match
analyzer suggests simpler alternatives over custom Match
es including
the all-new ToNullable
extension.
There's a handy Migration Guide available.
Result.GetOrThrow
EnumerableExtensions.GetNonEnumeratedCountOrNone
PriorityQueueExtensions.DequeueOrNone
PeekOrNone
The new Traverse
and Sequence
extension methods allow you to
«swap» the inner and outer monad (e.g. Result<Option<T>>
-> Option<Result<T>>
)
The new Memoize
extension function returns an IBuffer
/ IAsyncBuffer
.
This new type represents ownership over the underlying enumerator (and is therefore IDisposable
).
CycleRange
and RepeatRange
have also been changed to return an IBuffer
.
The parse extensions have been improved with the goal of aligning more with the BCL. Many of the changes are breaking.
- The functions now use BCL type names instead of C# type names
(e.g.
ParseIntOrNone
has been renamed toParse
) - The parameter names and nullability have been changed to align with the BCL.
- Added
HttpHeadersNonValidatedExtensions
Funcky now communicates materialization in the IEnumerable<T>
extensions by returning
IReadOnlyList
or IReadOnlyCollection
. This reduces «multiple enumeration» warnings.
Materialize
Chunk
Partition
Shuffle
SlidingWindow
Split
Transpose
Sequence.Return
Our Option<T>
type has always disallowed null
values.
This has been extended to our other monads: Result<T>
, Either<L, R>
and Reader<E, R>
.
Option.None()
has been changed to a property. There's an automatic fix available for this.- Our
Match
functions now differentiate betweenFunc
andAction
. TheAction
overloads have been renamed toSwitch
. - The return type of
EnumerableExtensions.ForEach
has been changed toUnit
. - Many parameter names and generic type names have been renamed in an attempt to unify naming across Funcky.
- All
Action
extensions have been moved to a new classActionExtensions
. EitherOrBoth
has been moved to theFuncky
namespace.- The retry policies have been moved to the
Funcky.RetryPolicies
namespace. Partition
returns a customPartitions
struct instead of a tuple.
APIs that have been obsoleted during 2.x have been removed:
ObjectExtensions.ToEnumerable
Funcky.GenericConstraints.RequireClass
andRequireStruct
- All
Try*
APIs (TryGetValue
,TryParse*
, etc.). These APIs use theOrNone
suffix instead. Sequence.Generate
has been superceded bySequence.Successors
CartesianProduct
We have removed the implicit System.Text.Json
converter for Option<T>
.
This means that you'll have to register the OptionJsonConverter
yourself.
All APIs related to IAsyncEnumerable
and Task
have been moved to the new Funcky.Async
package:
AsyncEnumerableExtensions
Functional.RetryAsync
->AsyncFunctional.RetryAsync
Option<Task>
andOption<ValueTask>
awaiters
This class exposes all of the same factory functions as Sequence
, but for IAsyncEnumerable
:
Return
Successors
Concat
Cycle
CycleRange
FromNullable
RepeatRange
We've worked hard towards the goal of parity between our extensions for IEnumerable
and IAsyncEnumerable
:
AdjacentGroupBy
AnyOrElse
AverageOrNoneAsync
/MaxOrNoneAsync
/MinOrNoneAsync
Chunk
ConcatToStringAsync
ExclusiveScan
InclusiveScan
Inspect
Interleave
Intersperse
JoinToStringAsync
MaterializeAsync
Memoize
Merge
NoneAsync
PartitionAsync
PowerSet
Sequence
/SequenceAsync
/Traverse
/TraverseAsync
ShuffleAsync
SlidingWindow
Split
Transpose
WhereNotNull
WithIndex
/WithLast
/WithPrevious
/WithFirst
ZipLongest
- Breaking: The
Is
prefix has been dropped from assertion methods for consistency with XUnit's naming scheme for assertion methods.
Option.None<T>()
: We originally introduced theOption.None<T>
method as a future proof replacement toOption<T>.None
for use in method groups, because Funcky 3 changesOption<T>.None
to a property. This turned out to be confusing to users especially because both method are always suggested in autocomplete.
This release is the last non-breaking release for Funcky before 3.0.
EnumerableExtensions.CartesianProduct
will be removed in Funcky 3.- To align our naming with that of the BCL, the
ParseOrNone
methods that return a type that has a keyword in C#int
,long
, etc. use the name of the BCL type instead.
Example:ParseIntOrNone
becomesParseInt32OrNone
.
The old methods will be removed in Funcky 3. - In preparation for Funcky 3 we deprecated
Option<T>.None
when used as method group. UseOption.None<T>
instead.
With the help of a source generator we have added a lot of new ParseOrNone methods for various types from the BCL:
- Unsigned integer types
DateOnly
,TimeOnly
Version
- Support for
ReadOnlySpan<T>
as input - ... and more
- Added implicit conversions for
Either
andResult
. - Implement
Inspect
forEither
andResult
. - Added
Partition
forIEnumerable<Either>
andIEnumerable<Result>
. - Added
ToString
onEither
andResult
. - Implement
ToEither
onOption
.
AnyOrElse
- Prefix sum:
InclusiveScan
andExclusiveScan
This release adds two new analyzer rules:
- λ1003: Warning when certain methods, such as
Match
are used without argument labels - λ1004: Warning that suggests
.ConcatToString()
over.JoinToString("")
Both of these warnings come with corresponding code fixes.
- Breaking: Funcky.Xunit now uses the
Funcky
namespace, instead ofFuncky.Xunit
. - Add assertion methods for testing
Either
:FunctionalAssert.IsLeft
andFunctionalAssert.IsRight
.
This release comes with a new package Funcky.Analyzers
, which we'll use
to guide users of Funcky
- Add extensions
DequeueOrNone
andPeekOrNone
onQueue
andConcurrentQueue
. - Add extension
ConcatToString
as an alias forstring.Concat
. - Add overload to
WhereSelect
with no parameter. - Add methods to convert from
Either
toOption
: #439LeftOrNone
: Returns the left value orNone
if the either value was right.RightOrNone
: Returns the right value orNone
if the either value was left.
- Extension functions for
System.Range
to allow the generations ofIEnumerable<T>
s from Range-Syntax:foreach(var i in 1..5) { } // negative numbers are not supported from x in 5..2 from y in 1..3 select (x, y)
Sequence.Return
now accepts multiple parameters:Sequence.Return(1, 2, 3)
⚠️ Sequence.Generate
has been deprecated in favour of the newly addedSequence.Successors
function which includes the first element (seed) in the generated sequence.
- Add
Option.FromBoolean
to create anOption<T>
from a boolean.
The behaviour of the Result.Error
constructor has been changed regarding exceptions
with an already set stack trace. The original stack trace is now preserved.
Previously this resulted in the stacktrace being replaced (.NET < 5.0) or an error (.NET ≥ 5.0).
- Add
Either.Flip
to swaps left with right.
- Funcky automatically adds global usings for the most important namespaces of funcky
when the
FunckyImplicitUsings
property is set. This requires .NET SDK ≥ 6.0 and C# ≥ 10.0. - Funcky now supports trimming for self-contained deployments.
Option<T>
now works with the new System.Text.Json source generation.- The
Funcky
package now supports Source Link and deterministic builds. - The symbols package is now finally working again.
This release includes the Reader
monad including a bunch of factory methods
and convenience extensions.
public static Reader<Enviroment, IEnumerable<string>> DefaultLayout(IEnumerable<DateTime> month)
=> from colorizedMonthName in ColorizedMonthName(month)
from weekDayLine in WeekDayLine()
from weeksInMonth in month
.GroupBy(GetWeekOfYear)
.Select(FormatWeek)
.Sequence()
select BuildDefaultLayout(colorizedMonthName, weekDayLine, weeksInMonth);
Funcky now supports Curry
, Uncurry
and Flip
for Action
s too.
This release also adds the inversion of ActionToUnit
: UnitToAction
Intersperse
: Adds a given item in between all items of an enumerable.JoinToString
: Alias forstring.Join
.WithPrevious
: Similar toWithFirst/Last/Index
but with the predecessor of each item.ForEach
: Add an overload toForEach
that accepts aUnit
-returningFunc
.
EitherOrBoth.FromOptions
creates anEitherOrBoth
from two options.Lazy.FromFunc
creates aLazy<T>
from aFunc
.
This is sugar over theLazy<T>
constructor, with the additional benefit of supporting type inference.Lazy.Return
creates aLazy<T>
from a value.
This is sugar over theLazy<T>
constructor, with the additional benefit of supporting type inference.
This release comes with a few small documentation improvements.
Funcky users will now also see the [Pure]
attributes which were previously not emitted.
- Remove upper bounds on all Microsoft.Bcl.* dependencies. Between the 2.3.0 and 2.4.0 release an overly restrictive upper bound was accidentally introduced for Microsoft.Bcl.AsyncInterfaces.
We've renamed all Try*
methods, such as TryParse
, TryGet
value to *OrNone
.
The old methods are still available, but marked as obsolete and will be removed in 3.0.0.
This release adds some new factory methods for creating IEnumerable<T>
to the Sequence
class:
Sequence.RepeatRange
: Generates a sequence that contains the same sequence of elements the given number of timesSequence.Cycle
: Cycles the same element over and over again as an endless generator.Sequence.CycleRange
: Generates a sequence that contains the same sequence of elements over and over again as an endless generatorSequence.Concat
Materialize
: Materializes all the items of a lazy enumerable.PowerSet
: Returns a sequence with the set of all subsetsShuffle
: Returns the given sequence in random Order in O(n).Split
: Splits the source sequence a separator.ZipLongest
: Zips two sequences with different lengths.
SplitLazy
: Splits a string by separator lazily.SplitLines
: Splits a string by newline lazily.
Curry
Uncurry
Flip
Compose
EitherOrBoth is a new data type that can represent Left
, Right
and Both
. It is used in ZipLongest
.
This release adds a Return
method for all monad types in Funcky:
Option.Return
Either<TLeft>.Return
Result.Return
To support more advanced comparison scenarios, OptionEqualityComparer
has been added similar to the already existing OptionComparer
.
- Added a missing
Match
overload toEither
that takesAction
s - Added additional overloads for
Functional.True
andFunctional.False
for up to four parameters.
net5.0
has been added to Funcky's target frameworks.
Option<T>
is now implicitly convertible fromT
.public static Option<int> Answer => 42;
Option
adds support forSystem.Text.Json
:
The customJsonConverter
is picked up automatically when serializing/deserializing.None
is serialized asnull
andSome(value)
is serialized to whatevervalue
serializes to.
This release adds factory methods for creating IEnumerable<T>
with the static class Sequence
:
Sequence.Return
: Creates anIEnumerable<T>
with exactly one item.Sequence.FromNullable
: Creates anIEnumerable<T>
with zero or one items.Sequence.Generate
: Creates anIEnumerable<T>
using a generation function and a seed.
This release adds a bunch of new extension methods on IEnumerable<T>
:
AdjacentGroupBy
AverageOrNone
CartesianProduct
Chunk
ElementAtOrNone
Interleave
MaxOrNone
Merge
MinOrNone
Pairwise
Partition
SlidingWindow
TakeEvery
Transpose
WithFirst
WithIndex
WithLast
This release adds a couple of extension methods that provide interoperability
with Option<T>
to IAsyncEnumerable<T>
:
WhereSelect
FirstOrNoneAsync
LastOrNoneAsync
SingleOrNoneAsync
ElementAtOrNoneAsync
A couple of the new extension methods on IEnumerable<T>
have async counterparts:
Pairwise
TakeEvery
The naming of the extension methods and their overloads follows that of System.Linq.Async
.
This release adds specialized extension methods for IQueryable<T>
that are better
suited especially for use with EF Core:
FirstOrNone
LastOrNone
SingleOrNone
To support .NET Standard, Funcky conditionally pulls in dependencies that provide the missing functionality:
Microsoft.Bcl.AsyncInterfaces
for .NET Standard 2.0System.Collections.Immutable
andSystem.Text.Json
for .NET Standard 2.0 and 2.1- The version constraints for all these packages have been relaxed to allow 5.x.
ConfigureAwait(false)
is now used everywhereawait
is used.- The
IRetryPolicy
implementations now use correctTimespan
withdouble
multiplication when targeting .NET Standard 2.0.
ObjectExtensions.ToEnumerable
has been deprecated in favor ofSequence.FromNullable
.RequireClass
andRequireStruct
have been obsoleted with no replacement.
- Added overload to
Functional.Retry
with aIRetryPolicy
. - Added
None
overload that takes no predicate.
- Re-release of previous release with correct assemblies.
- Add
Inspect
method toOption
akin toIEnumerable.Inspect
. - Add
ToTheoryData
extension forIEnumerable<T>
for xUnit. - Add
Unit.Value
as a way to a get aUnit
value. - Add
Functional.Retry
which retries a producer untilOption.Some
is returned.
- Remove
Reader
monad based onawait
. - Remove
IToString
. - Remove overload for
Option.From
that flattens passedOption
s. - Move
ToEnumerable
extension method to its own class. This is only a breaking change if you've used the extension method as normal method. In that case you need to changeEnumerableExtensions.ToEnumerable
toObjectExtensions.ToEnumerable
. - Rename
Option.From
toOption.FromNullable
and remove overload that takes non-nullable value types. - Unify
Option<T>.ToEnumerable
andYield
toToEnumerable
- Rename
OrElse
overloads that return the item toGetOrElse
which improves overload resolution. - The
Each
extension method onIEnumerable<T>
has been renamed toForEach
. - Move the
Ok
constructor ofResult<T>
to a non-generic class. This allows for the compiler to infer the generic type. Old:Result<int>.Ok(10)
. New:Result.Ok(10)
. - Use
Func<T, bool>
instead ofPredicate<T>
in predicate composition functions (Functional.All
,Functional.Any
,Functional.Not
), because most APIs inSystem
useFunc
. Functional.Any
now returnsfalse
when the given list of predicates is empty.
- Fix incorrect
Equals
implementation onOption
.Equals
previously returnedtrue
when comparing aNone
value with aSome
value containing the default value of the type. Exception
created byResult
monad contains valid stack trace- Fix incorrect implementation on
Result.SelectMany
which called theselectedResultSelector
even when the result was an error. As a result (pun intended) of the fix,ResultCombinationException
is no longer needed and also removed.
- Add
IndexOfOrNone
,LastIndexOfOrNone
,IndexOfAnyOrNone
andLastIndexOfAnyOrNone
extension methods tostring
. - Added
Curry
,Uncurry
andFlip
to theFunctional
Class - Add extension method for
HttpHeaders.TryGetValues
, which returns anOption
. - Add extension methods for getting
Stream
properties that are not always available, asOption
:GetLengthOrNone
,GetPositionOrNone
,GetReadTimeoutOrNone
,GetWriteTimeoutOrNone
. - Add
None
extension method toIEnumerable
. Option<Task<T>>
,Option<Task>
and theirValueTask
equivalents are now awaitable:var answer = await Option.Some(Task.FromResult(42));
- Full nullable support introduced with C# 8.
- Mark our functions as
[Pure]
. - Implement
IEquatable
onOption
,Result
andEither
.
- Move the
Ok
constructor ofResult<T>
to a non-generic class. This allows for the compiler to infer the generic type. Old:Result<int>.Ok(10)
. New:Result.Ok(10)
. - Add
IndexOfOrNone
,LastIndexOfOrNone
,IndexOfAnyOrNone
andLastIndexOfAnyOrNone
extension methods tostring
. - Rename
OrElse
overloads that return the item toGetOrElse
which improves overload resolution. - Added
Curry
,Uncurry
andFlip
to theFunctional
Class - Remove
IToString
. - Mark our functions as
[Pure]
. - Fix incorrect implementation on
Result.SelectMany
which called theselectedResultSelector
even when the result was an error. As a result (pun intended) of the fix,ResultCombinationException
is no longer needed and also removed.
- Full nullable support introduced with C# 8
- Rename
Option.From
->Option.FromNullable
and remove overload that takes non-nullable value types. - Use
Func<T, bool>
instead ofPredicate<T>
in predicate composition functions (Functional.All
,Functional.Any
,Functional.Not
), because most APIs inSystem
useFunc
. Functional.Any
now returnsfalse
when the given list of predicates is empty.- The
Each
extension method onIEnumerable<T>
has been renamed toForEach
. - Unify
Option<T>.ToEnumerable
andYield
toToEnumerable
- Remove
Reader
monad based onawait
. Exception
created byResult
monad contains valid stack trace
- Added overload for
AndThen
which flattens theOption
- Add
Where
method toOption<T>
, which allows filtering theOption
by a predicate. - Add overload for
Option<T>.SelectMany
that takes only a selector. - Add
WhereNotNull
extension method forIEnumerable<T>
.
- Add nullability annotations to everything except for
Monads.Reader
. - Add a function for creating an
Option<T>
from a nullable value:Option.From
. Either.Match
now throws when called on anEither
value created usingdefault(Either<L, R>)
.- Add
True
andFalse
functions to public API - Match of
Result
Monad accepts actions - Add
FirstOrNone
,LastOrNone
andSingleOrNone
extension functions
- Add ToEnumerable function to
Option<T>
. - Add
WhereSelect
extension function forIEnumerable<T>
. - Add missing overload for nullary actions to
ActionToUnit
.