runtime.csharp.IRT.Either.cs Maven / Gradle / Ivy
using System;
// TODO Consider moving out of here the converter attribute
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
using IRT.Marshaller;
namespace IRT {
[JsonConverter(typeof(Either_JsonNetConverter))]
public abstract class Either {
public abstract Either Map(Func f);
public abstract Either BiMap(Func g, Func f);
public abstract B Fold(Func whenRight, Func whenLeft);
public abstract L GetOrElse(L l);
public abstract L GetLeft();
public abstract A GetOrElse(A a);
public abstract A GetRight();
public abstract bool IsLeft();
public abstract bool IsRight();
public abstract Either Swap();
public abstract Either FilterOrElse(Func p, L zero);
public abstract void Match(Action whenRight, Action whenLeft);
public static explicit operator L(Either e) {
if (!e.IsLeft()) {
throw new InvalidCastException("Either is not in the Left state.");
}
return e.GetLeft();
}
public static explicit operator A(Either e) {
if (e.IsLeft()) {
throw new InvalidCastException("Either is not in the Right state.");
}
return e.GetRight();
}
// We support both sides to have equal type, for example if both
// success and failure hold a simple Message class. In that case
// same operator would fail, so we move it to the generated class.
public static implicit operator Either (L value) {
return new Left(value);
}
public static implicit operator Either (A value) {
return new Right(value);
}
public sealed class Left: Either {
private readonly L Value;
public Left(L value) {
Value = value;
}
public override Either Map(Func f) {
return new Either.Left(Value);
}
public override Either BiMap(Func g, Func f) {
return new Either.Left(f(Value));
}
public override B Fold(Func whenRight, Func whenLeft) {
return whenLeft(Value);
}
public override L GetOrElse(L l) {
return Value;
}
public override A GetOrElse(A a) {
return a;
}
public override L GetLeft() {
return Value;
}
public override A GetRight() {
throw new InvalidCastException("Either is not in the Right state.");
}
public override bool IsLeft() {
return true;
}
public override bool IsRight() {
return false;
}
public override Either Swap() {
return new Either.Right(Value);
}
public override Either FilterOrElse(Func p, L zero) {
return this;
}
public override void Match(Action whenRight, Action whenLeft) {
whenLeft(Value);
}
}
public sealed class Right: Either {
private readonly A Value;
public Right(A value) {
Value = value;
}
public override Either Map(Func f) {
return new Either.Right(f(Value));
}
public override Either BiMap(Func g, Func f) {
return new Either.Right(g(Value));
}
public override B Fold(Func whenRight, Func whenLeft) {
return whenRight(Value);
}
public override L GetOrElse(L l) {
return l;
}
public override A GetOrElse(A a) {
return Value;
}
public override L GetLeft() {
throw new InvalidCastException("Either is not in the Left state.");
}
public override A GetRight() {
return Value;
}
public override bool IsLeft() {
return false;
}
public override bool IsRight() {
return true;
}
public override Either Swap() {
return new Either.Left(Value);
}
public override Either FilterOrElse(Func p, L zero) {
if (p(Value)) {
return this;
}
return new Left(zero);
}
public override void Match(Action whenRight, Action whenLeft) {
whenRight(Value);
}
}
}
// TODO Consider moving out of here to the marshaller itself
// This implementation seems to trigger some issues with Json.net, as it doesn't know how to
// deserialize a generic type like this...
// public class Either_JsonNetConverter: JsonNetConverter> {
// public override void WriteJson(JsonWriter writer, Either al, JsonSerializer serializer) {
// writer.WriteStartObject();
// if (al.IsLeft()) {
// writer.WritePropertyName("Failure");
// var l = al.GetLeft();
// if (typeof(L).IsInterface) {
// // Serializing polymorphic type
// writer.WriteStartObject();
// writer.WritePropertyName((l as IRTTI).GetFullClassName());
// serializer.Serialize(writer, l);
// writer.WriteEndObject();
// } else {
// serializer.Serialize(writer, l);
// }
// } else {
// writer.WritePropertyName("Success");
// var r = al.GetRight();
// if (typeof(R).IsInterface) {
// // Serializing polymorphic type
// writer.WriteStartObject();
// writer.WritePropertyName((r as IRTTI).GetFullClassName());
// serializer.Serialize(writer, r);
// writer.WriteEndObject();
// } else {
// serializer.Serialize(writer, r);
// }
// }
// writer.WriteEndObject();
// }
// public override Either ReadJson(JsonReader reader, System.Type objectType, Either existingValue, bool hasExistingValue, JsonSerializer serializer) {
// var json = JObject.Load(reader);
// var kv = json.Properties().First();
// switch (kv.Name) {
// case "Success": {
// var v = serializer.Deserialize(kv.Value.CreateReader());
// return new Either.Right(v);
// }
// case "Failure": {
// var v = serializer.Deserialize(kv.Value.CreateReader());
// return new Either.Left(v);
// }
// default:
// throw new System.Exception("Unknown either Either type: " + kv.Name);
// }
// }
// }
public class Either_JsonNetConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Type valueType = value.GetType();
Type[] genArgs = valueType.GetGenericArguments();
writer.WriteStartObject();
if ((bool)valueType.GetMethod("IsLeft").Invoke(value, new object[0])) {
writer.WritePropertyName("Failure");
var l = valueType.GetMethod("GetLeft").Invoke(value, new object[0]);
if (genArgs[0].IsInterface) {
// Serializing polymorphic type
writer.WriteStartObject();
writer.WritePropertyName((l as IRTTI).GetFullClassName());
serializer.Serialize(writer, l);
writer.WriteEndObject();
} else {
serializer.Serialize(writer, l);
}
} else {
writer.WritePropertyName("Success");
var r = valueType.GetMethod("GetRight").Invoke(value, new object[0]);
if (genArgs[1].IsInterface) {
// Serializing polymorphic type
writer.WriteStartObject();
writer.WritePropertyName((r as IRTTI).GetFullClassName());
serializer.Serialize(writer, r);
writer.WriteEndObject();
} else {
serializer.Serialize(writer, r);
}
}
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Type[] genArgs = objectType.GetGenericArguments();
var json = JObject.Load(reader);
var kv = json.Properties().First();
switch (kv.Name) {
case "Success": {
var v = serializer.Deserialize(kv.Value.CreateReader(), genArgs[1]);
var rightType = typeof(Either<,>.Right).MakeGenericType(genArgs);
return Activator.CreateInstance(rightType, v);
}
case "Failure": {
var v = serializer.Deserialize(kv.Value.CreateReader(), genArgs[0]);
var leftType = typeof(Either<,>.Left).MakeGenericType(genArgs);
return Activator.CreateInstance(leftType, v);
}
default:
throw new System.Exception("Unknown either Either type: " + kv.Name);
}
}
public override bool CanConvert(Type objectType)
{
return typeof(Either<,>).IsAssignableFrom(objectType);
}
}
}