Skip to content

Instantly share code, notes, and snippets.

@panesofglass
Last active March 7, 2018 21:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panesofglass/88238e0c2bc39fdb063d3acd011dc0d2 to your computer and use it in GitHub Desktop.
Save panesofglass/88238e0c2bc39fdb063d3acd011dc0d2 to your computer and use it in GitHub Desktop.
Use cases for .NET Interface Extensions

Static Methods on Interfaces

Use Cases

  1. Define related sets or sequences of algorithms to be defined together (need explicit example)
  2. Specify required static extension methods for implementing things like LINQ or Rx.
  3. Transducers cannot currently be implemented efficiently in .NET. Transducers would need to use IEnumerable<_> as a base collection type rather than potentially more efficient implementations (or retaining the input's stype, e.g. List<_>, etc).
  4. F# modules such as List, Array, and Seq all provide functions that are the same in nature but differ by wrapper type. However, it is not possible for a new collection type to ensure it adheres to this same specification without manually checking for completeness.

Extension Interfaces

Extension interfaces would allow the implementation of an interface to exist apart from a type definition and would obviate the need for techniques like "Assembly Neutral Interfaces". This feature would provide a more generic form of type classes for implementing functors, monads, etc. or mixins/traits for those looking for ways to adapt existing types without explicit boilerplate to adapt types (and avoiding allocations).

Use Cases

  1. Re-use existing types to implement new interfaces. Example: define a new generic IListLike that exposes a set of required methods for X domain and use existing types such as List<_>, Array<_>, etc. as implementations.
  2. Allow implementors to extend a type into an interface based on their own needs rather that the original author's. Example: specify interfaces for HTTP requests and responses that may be implemented as structs in one library, classes in another, F# records in an F# representation, or even implemented as an OWIN dictionary.
  3. Free monads could be implemented without a lot of boilerplate.
// Assumes type equivalence in the runtime
// If not, explicitly map types:
extension IEnumerable<T> : IListLike<T>;
extension T[] : IListLike<T>;
extension Enumerable : ISelectable<IEnumerable<_>>;
// Marker interface only for illustration
public interface IListLike<T>
{
}
public interface ISelectable<TSource, TIn, TResult, TOut> where TIn : IListLike<TSource>, where TOut : IListLike<TResult>
{
static TOut Select<TSource, TResult>(TIn data, Func<TSource, TResult> selector);
static TOut Select<TSource, TResult>(TIn data, Func<TSource, Int32, TResult> selector);
}
public static class ArraySelectable<TSource, TResult> : ISelectable<TSource, TSource[], TResult, TResult[]>
{
public static TResult[] Select<TSource, TResult>(TSource[] data, Func<TSource, TResult> selector)
{
var result = new TResult[this.Length];
for (var i = 0; i < this.Length; i++)
{
result[i] = selector(this[i]);
}
return result;
}
public static TResult[] Select<TSource, TResult>(TSource[] data, Func<TSource, Int32, TResult> selector)
{
var result = new TResult[this.Length];
for (var i = 0; i < this.Length; i++)
{
result[i] = selector(this[i], i);
}
return result;
}
}
public class Test<TElememnt, TCollection, TModule> where TCollection : IListLike<TElement>, where TModule : ISelectable
{
public static TCollection Identity(TCollection data)
{
return TModule.Select(data, d => d);
}
}
// Use Enumerable:
using EnumerableTest = Test<IEnumerable<int>, Enumerable>;
var data = Enumerable.Range<int>(1, 10);
EnumerableTest.Identity(data);
// Use ArraySelectable:
var data = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Test<int[], ArraySelectable>.Identity(data);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment