Created
May 16, 2017 15:09
-
-
Save handcraftsman/5ff5ae0f698d9b50ee033e66bea3acca to your computer and use it in GitHub Desktop.
c# Railway oriented Programming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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