Skip to content

Instantly share code, notes, and snippets.

@handcraftsman
Created May 16, 2017 15:09
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 handcraftsman/5ff5ae0f698d9b50ee033e66bea3acca to your computer and use it in GitHub Desktop.
Save handcraftsman/5ff5ae0f698d9b50ee033e66bea3acca to your computer and use it in GitHub Desktop.
c# Railway oriented Programming
/// <summary>
/// partial implementation of Railway oriented Programming in C#.
/// based on: https://www.slideshare.net/ScottWlaschin/railway-oriented-programming?ref=http://fsharpforfunandprofit.com/rop/
/// Notification<T> is defined in https://github.com/mvbalaw/MvbaCore/blob/master/src/MvbaCore/Notification.cs
///
/// Unfortunately I couldn't find very many functions in my code where this could even be applied.
/// Reasons include the function having multiple inputs, throwing an exception, triggering an async action, etc.,
/// all of which would require additional growth in this infrastructure and at least in my opinion do not make
/// the code easier to understand, debug or maintain.
/// </summary>
public static class Railway
{
public static RailwaySwitch<TInput, TResult> For<TInput, TResult>(TInput output)
{
return new RailwaySwitch<TInput, TResult>(output);
}
}
public class RailwaySwitch<TCurrent, TResult>
{
private readonly Notification<TCurrent> _notification;
private readonly dynamic _parent;
private Notification<TResult> _result;
private RailwaySwitch(Notification<TCurrent> notification, dynamic parent)
{
_notification = notification;
_parent = parent;
TrySetResult(parent);
}
private RailwaySwitch(TCurrent item, dynamic parent)
{
_parent = parent;
TrySetResult(parent);
_notification = new Notification<TCurrent>
{
Item = item
};
}
private RailwaySwitch(Exception exception, dynamic parent)
{
_parent = parent;
TrySetResult(parent);
_notification = new Notification<TCurrent>(Notification.ErrorFor(exception.Message));
}
public RailwaySwitch(TCurrent item)
{
_notification = new Notification<TCurrent>
{
Item = item
};
}
public RailwaySwitch<TPreviousType, TResult> SwitchBackTo<TPreviousType>()
{
if (typeof(TPreviousType) == typeof(TCurrent))
{
return this as RailwaySwitch<TPreviousType, TResult>;
}
if (_parent == null)
{
throw new ArgumentException("No ancestors of type " + typeof(TPreviousType).Name);
}
return _parent.BacktrackTo<TPreviousType>();
}
public RailwaySwitch<TNext, TResult> SwitchTo<TNext>(Func<TCurrent, Notification<TNext>> @do)
{
if (!_notification.IsValid || _result != null)
{
return new RailwaySwitch<TNext, TResult>(new Notification<TNext>(_notification), this);
}
return new RailwaySwitch<TNext, TResult>(@do(_notification.Item), this);
}
public RailwaySwitch<TNext, TResult> SwitchTo<TNext>(Func<TCurrent, TNext> @do)
{
if (!_notification.IsValid || _result != null)
{
return new RailwaySwitch<TNext, TResult>(new Notification<TNext>(_notification), this);
}
try
{
return new RailwaySwitch<TNext, TResult>(@do(_notification.Item), this);
}
catch (Exception ex)
{
return new RailwaySwitch<TNext, TResult>(ex, this);
}
}
public TResult Stop()
{
return _result.Item;
}
public RailwaySwitch<TCurrent, TResult> StopIf(Func<TCurrent, bool> matches, Func<TResult> nextStep)
{
if (matches(_notification.Item))
{
try
{
_result = new Notification<TResult>
{
Item = nextStep()
};
}
catch (Exception ex)
{
return new RailwaySwitch<TCurrent, TResult>(ex, this);
}
}
return this;
}
public RailwaySwitch<TCurrent, TResult> Update(Action<TCurrent> updateWith)
{
if (_notification.IsValid && _result == null)
{
updateWith(_notification.Item);
}
return this;
}
private void TrySetResult(dynamic parent)
{
var result = parent as Notification<TResult>;
if (result != null)
{
_result = result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment