C# Covariance and what has to do with Functional C#
No covariance is not a hard concept.Its is actually very easy to grasp.
Source : https://gist.github.com/dimitris-papadimitriou-chr/578f76c2d4b8d18f6f7fc8e69dcf0153
No covariance is not a hard concept.Its is actually very easy to grasp.
What is covariance in C# and what has to do with functional C#.
The Problem
Assume that you have this simple
public interface IWithSuccess
{
public bool IsSuccesfull { get; }
}public class SomeResult : IWithSuccess
{
public bool IsSuccesfull => true;
}
Amazingly you cannot write the following code even if you might Expected to:
Func<Task<SomeResult>> GetSomethingLazyAsync() =>...
//Error CS0029 Cannot implicitly convert types !!!!!!!!!
Func<Task<IWithSuccess>> lazyResult= GetSomethingLazyAsync();
Assign something that might look like more specific to something that seems more general in nature.
Assign a Func<Task<SomeResult>>
to a Func<Task<
IWithSuccess>>
as you can assign a SomeResult
to an IWithSuccess
if you go to the Func<_>
declaration you might see the following:
public delegate TResult Func<out TResult>();
which you should keep in mind because this means that an assignment of
Func<SomeResult>
to a Func<IWithSuccess>
is perfectly fine and you can use this. But the Task spoils all the Fun(c) here.
My Favorite Solution
Use the adapter pattern in a minimal form and Extension methods to Chain conversion.
1) First generate the following
public interface IFuncTask<out T>
{
T GetResult();
}
public class FuncTaskAdapter<T> : IFuncTask<T>
{ public FuncTaskAdapter(Func<Task<T>> funcTask) =>
this.funcTask = funcTask; public T GetResult() => funcTask().Result;
}
2)Now we can use Extension methods to make a conversion under the hood
public static partial class FunctionalExtensions
{
public static IFuncTask<T> ToWithResult<T>(this Func<Task<T>> @this) => new FuncTaskAdapter<T>(@this);
}
3) now we can write :
IFuncTask<IWithSuccess> t = GetSomethingLazyAsync()
.ToWithResult();
covariance works now !!!
what i did is that i made the decision to sacrifice the knowledge that there is a Task there, with the minimal ability to get a Result if i ever need it.
public T GetResult() => funcTask().Result;
Obviously this conversion is usefully only at the late stages of computation where we are reaching a conclusion on the outcome and we no longer need any Task related ‘capabilities’ . For example we can add the following extension method on top of IFuncTask<>
public static T GetOrThrowIfFailed<T>(this FuncTask<T> @this, string message) where T : IWithResult
{
var value = @this(); // this might also throw exception from Task.
return value?.IsSuccesfull ?? false ? value :
throw new Exception(message);
}
and use this like this :
//ok this is covariant on T
FuncTask<IWithResult> lazyResult = GetSomethingLazyAsync().ToFuncTask();
SomeResult result = GetSomethingLazyAsync().ToFuncTask().GetOrThrowIfFailed("failed to get");
of course the Great advantage is what i call ‘Syntax extensibility’ and ‘Syntax coupling’. When i say syntax extensibility is the ability of functional syntax to be decorated with minimal modification in code. Compared to classical syntax, which is a mess and the cross cutting concerns or different intentional concerns are coupled with the computation.
Syntax Extension
For example we could create a simple extension method called GetOrThrow
build on top of GetOrThrowIfNull, aka decorates GetOrThrowIfNull (aka Functional Decorator pattern) that simply adds an additional try/catch for the Exception or Func failures
public static T GetOrThrow<T>(this IFuncTask<T> @this, string message)
{
try
{
return @this.GetOrThrowIfNull(message);
}
catch (Exception e) //Please Dont hide Exceptions
{
throw new Exception(message);
}
}
in the functional syntax this would be applied by simply
SomeResult result = GetSomethingLazyAsync()
.ToWithResult()
.GetOrThrow("failed to get");
whereas in classical syntax this would force us to do the following
SomeResult result;
try
{
var lazyTask = GetSomethingLazyAsync();
result = await lazyTask();
if (result is null)
throw new Exception("failed to get Anything");
}
catch (Exception e) //Please Dont hide Exceptions
{
throw new Exception("failed to get Anything");
}
do you want to see more thoughts on this. check out this : “Delegates as Adapters C# : More covariance”
How do i come up with those coding insight? By mastering the essentials of Functional C# https://leanpub.com/functional-programming-in-cSharp-with-categories