-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
369 additions
and
68 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
namespace Fraktalio.FModel.Contracts; | ||
|
||
/// <summary> | ||
/// View interface | ||
/// </summary> | ||
/// <typeparam name="S"></typeparam> | ||
/// <typeparam name="E"></typeparam> | ||
public interface IView<S, in E> | ||
{ | ||
Func<S, E, S> Evolve { get; } | ||
S InitialState { get; } | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
namespace Fraktalio.FModel; | ||
|
||
/// <summary> | ||
/// [InternalView] is a datatype that represents the event handling algorithm, | ||
/// responsible for translating the events into denormalized state, | ||
/// which is more adequate for querying. | ||
/// | ||
/// It has three generic parameters [Si], [So], [E], representing the type of the values that [InternalView] may contain or use. | ||
/// [InternalView] can be specialized for any type of [Si], [So], [E] because these types does not affect its behavior. | ||
/// [InternalView] behaves the same for [E]=[Int] or [E]=YourCustomType, for example. | ||
/// | ||
/// [InternalView] is a pure domain component | ||
/// </summary> | ||
/// <param name="evolve">A pure function/lambda that takes input state of type [Si] and input event of type [E] as parameters, and returns the output/new state [So]</param> | ||
/// <param name="initialState">A starting point / An initial state of type [So]</param> | ||
/// <typeparam name="Si">Input State type</typeparam> | ||
/// <typeparam name="So">Output State type</typeparam> | ||
/// <typeparam name="E">Event type</typeparam> | ||
internal class InternalView<Si, So, E>(Func<Si, E, So> evolve, So initialState) | ||
{ | ||
internal Func<Si, E, So> Evolve { get; } = evolve; | ||
internal So InitialState { get; } = initialState; | ||
|
||
/// <summary> | ||
/// Left map on E/Event parameter - Contravariant | ||
/// </summary> | ||
/// <param name="f">Map function</param> | ||
/// <typeparam name="En">En Event new</typeparam> | ||
/// <returns>Mapped view</returns> | ||
internal InternalView<Si, So, En> MapLeftOnEvent<En>(Func<En, E> f) => | ||
new( | ||
(si, en) => Evolve(si, f(en)), | ||
InitialState | ||
); | ||
|
||
/// <summary> | ||
/// Dimap on S/State parameter - Contravariant on the Si (input State) - Covariant on the So (output State) = Profunctor | ||
/// </summary> | ||
/// <param name="fl"></param> | ||
/// <param name="fr"></param> | ||
/// <typeparam name="Sin">Sin State input new</typeparam> | ||
/// <typeparam name="Son">Son State output new</typeparam> | ||
/// <returns></returns> | ||
public InternalView<Sin, Son, E> DimapOnState<Sin, Son>(Func<Sin, Si> fl, Func<So, Son> fr) => | ||
new( | ||
(sin, e) => fr(Evolve(fl(sin), e)), | ||
fr(InitialState) | ||
); | ||
|
||
/// <summary> | ||
/// Left map on S/State parameter - Contravariant | ||
/// </summary> | ||
/// <param name="f"></param> | ||
/// <typeparam name="Sin">Sin State input new</typeparam> | ||
/// <returns></returns> | ||
public InternalView<Sin, So, E> MapLeftOnState<Sin>(Func<Sin, Si> f) => DimapOnState(f, so => so); | ||
|
||
/// <summary> | ||
/// Right map on S/State parameter - Covariant | ||
/// </summary> | ||
/// <param name="f"></param> | ||
/// <typeparam name="Son"></typeparam> | ||
/// <returns></returns> | ||
public InternalView<Si, Son, E> MapOnState<Son>(Func<So, Son> f) => DimapOnState<Si, Son>(si => si, f); | ||
|
||
/// <summary> | ||
/// Apply on S/State parameter - Applicative | ||
/// </summary> | ||
/// <param name="ff"></param> | ||
/// <typeparam name="Son">Son State output new type</typeparam> | ||
/// <returns></returns> | ||
public InternalView<Si, Son, E> ApplyOnState<Son>(InternalView<Si, Func<So, Son>, E> ff) => | ||
new( | ||
(si, e) => ff.Evolve(si, e)(Evolve(si, e)), | ||
ff.InitialState(InitialState) | ||
); | ||
|
||
internal InternalView<Si, Tuple<So, Son>, E> ProductOnState<Son>(InternalView<Si, Son, E> fb) => | ||
ApplyOnState(fb.MapOnState(b => new Func<So, Tuple<So, Son>>(a => new Tuple<So, Son>(a, b)))); | ||
|
||
|
||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
namespace Fraktalio.FModel; | ||
|
||
internal static class InternalViewExtensions | ||
{ | ||
/// <summary> | ||
/// Combines [InternalView]s into one bigger [InternalView] | ||
/// </summary> | ||
/// <param name="x">first view</param> | ||
/// <param name="y">second view</param> | ||
/// <typeparam name="Si">Si State input of the first View</typeparam> | ||
/// <typeparam name="So">So State output of the first View</typeparam> | ||
/// <typeparam name="E">E Event of the first View</typeparam> | ||
/// <typeparam name="Si2">Si2 State input of the second View</typeparam> | ||
/// <typeparam name="So2">So2 State output of the second View</typeparam> | ||
/// <typeparam name="E2">E2 Event of the second View</typeparam> | ||
/// <typeparam name="E_SUPER">E_SUPER super type for [E] and [E2]</typeparam> | ||
/// <returns>new View of type [InternalView]<[Pair]<[Si], [Si2]>, [Pair]<[So], [So2]>, [E_SUPER]></returns> | ||
internal static InternalView<Tuple<Si, Si2>, Tuple<So, So2>, E_SUPER> Combine<Si, So, E, Si2, So2, E2, E_SUPER>( | ||
this InternalView<Si, So, E?> x, InternalView<Si2, So2, E2?> y) | ||
where E : class, E_SUPER | ||
where E2 : class, E_SUPER | ||
{ | ||
var viewX = x.MapLeftOnEvent<E_SUPER>(e => e as E).MapLeftOnState<Tuple<Si, Si2>>(pair => pair.Item1); | ||
var viewY = y.MapLeftOnEvent<E_SUPER>(e => e as E2).MapLeftOnState<Tuple<Si, Si2>>(pair => pair.Item2); | ||
|
||
return viewX.ProductOnState(viewY); | ||
} | ||
|
||
internal static View<S, E> AsView<S, E>(this InternalView<S, S, E> view) => new(view.Evolve, view.InitialState); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using Fraktalio.FModel.Contracts; | ||
|
||
namespace Fraktalio.FModel; | ||
|
||
/// <summary> | ||
/// [View] is a datatype that represents the event handling algorithm, | ||
/// responsible for translating the events into denormalized state, | ||
/// which is more adequate for querying. | ||
/// | ||
/// It has two generic parameters `S`, `E`, representing the type of the values that [View] may contain or use. | ||
/// [View] can be specialized for any type of `S`, `E` because these types does not affect its behavior. | ||
/// [View] behaves the same for `E`=[Int] or `E`=`YourCustomType`. | ||
/// </summary> | ||
/// <param name="evolve">evolve A pure function/lambda that takes input state of type [S] and input event of type [E] as parameters, and returns the output/new state [S]</param> | ||
/// <param name="initialState">initialState A starting point / An initial state of type [S]</param> | ||
/// <typeparam name="S">State type</typeparam> | ||
/// <typeparam name="E">Event type</typeparam> | ||
public class View<S, E>(Func<S, E, S> evolve, S initialState) : IView<S, E> | ||
{ | ||
public Func<S, E, S> Evolve { get; } = evolve; | ||
public S InitialState { get; } = initialState; | ||
|
||
/// <summary> | ||
/// Left map on E/Event | ||
/// </summary> | ||
/// <param name="f">Function that maps type `En` to `E`</param> | ||
/// <typeparam name="En">En Event new</typeparam> | ||
/// <returns>New View of type [View]<[S], [En]></returns> | ||
public View<S, En> MapLeftOnEvent<En>(Func<En, E> f) => | ||
new InternalView<S, S, E>(Evolve, InitialState).MapLeftOnEvent(f).AsView(); | ||
|
||
/// <summary> | ||
/// Di-map on S/State | ||
/// </summary> | ||
/// <param name="fl">Function that maps type `Sn` to `S`</param> | ||
/// <param name="fr">Function that maps type `S` to `Sn`</param> | ||
/// <typeparam name="Sn">Sn State new</typeparam> | ||
/// <returns>New View of type [View]<[Sn], [E]></returns> | ||
public View<Sn, E> DimapOnState<Sn>(Func<Sn, S> fl, Func<S, Sn> fr) => | ||
new InternalView<S, S, E>(Evolve, InitialState).DimapOnState(fl, fr).AsView(); | ||
} | ||
|
||
public static class ViewExtensions | ||
{ | ||
/// <summary> | ||
/// Combines [View]s into one [View] | ||
/// | ||
/// Possible to use when [E] and [E2] have common superclass [E_SUPER] | ||
/// </summary> | ||
/// <param name="x"></param> | ||
/// <param name="y"></param> | ||
/// <typeparam name="S">State of the first View</typeparam> | ||
/// <typeparam name="E">Event of the first View</typeparam> | ||
/// <typeparam name="S2">State of the second View</typeparam> | ||
/// <typeparam name="E2">Event of the second View</typeparam> | ||
/// <typeparam name="E_SUPER">Super type for [E] and [E2]</typeparam> | ||
/// <returns></returns> | ||
public static View<Tuple<S, S2>, E_SUPER> Combine<S, E, S2, E2, E_SUPER>(this View<S, E?> x, View<S2, E2?> y) | ||
where E : class, E_SUPER | ||
where E2 : class, E_SUPER | ||
{ | ||
var internalViewX = new InternalView<S, S, E?>(x.Evolve, x.InitialState); | ||
var internalViewY = new InternalView<S2, S2, E2?>(y.Evolve, y.InitialState); | ||
var combined = internalViewX.Combine<S, S, E, S2, S2, E2, E_SUPER>(internalViewY); | ||
return combined.AsView(); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
namespace Fraktalio.FModel; | ||
|
||
internal class ViewBuilder<S, E> | ||
{ | ||
private Func<S, E, S> Evolve { get; set; } = (s, _) => s; | ||
private Func<S> InitialState { get; set; } = () => throw new Exception("Initial State is not initialized"); | ||
|
||
public void SetEvolve(Func<S, E, S> value) => Evolve = value; | ||
|
||
public void SetInitialState(Func<S> value) => InitialState = value; | ||
|
||
public View<S, E> Build() => new(Evolve, InitialState()); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace Fraktalio.FModel; | ||
|
||
internal static class ViewFactory | ||
{ | ||
public static View<S, E> CreateView<S, E>(Action<ViewBuilder<S, E>> buildAction) | ||
{ | ||
var builder = new ViewBuilder<S, E>(); | ||
buildAction(builder); | ||
return builder.Build(); | ||
} | ||
} |
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
15 changes: 15 additions & 0 deletions
15
test/Fraktalio.FModel.Tests/Examples/Numbers/Even/EvenNumberView.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace Fraktalio.FModel.Tests.Examples.Numbers.Even; | ||
|
||
public class EvenNumberView() : View<EvenNumberState, EvenNumberEvent?>( | ||
(s, e) => | ||
{ | ||
return e switch | ||
{ | ||
EvenNumberAdded => new EvenNumberState(s.Description + e.Description, | ||
s.Value + e.Value), | ||
EvenNumberSubtracted => new EvenNumberState(s.Description - e.Description, | ||
s.Value - e.Value), | ||
_ => s | ||
}; | ||
}, | ||
new EvenNumberState(Description.Create("Initial state"), Number.Create(0))); |
3 changes: 0 additions & 3 deletions
3
test/Fraktalio.FModel.Tests/Examples/Numbers/EvenNumberState.cs
This file was deleted.
Oops, something went wrong.
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
37 changes: 12 additions & 25 deletions
37
test/Fraktalio.FModel.Tests/Examples/Numbers/NumberCommand.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,24 @@ | ||
namespace Fraktalio.FModel.Tests.Examples.Numbers; | ||
|
||
public abstract class NumberCommand | ||
public abstract class NumberCommand(Description description, Number number) | ||
{ | ||
public abstract Description Description { get; } | ||
public abstract Number Value { get; } | ||
public Description Description { get; } = description; | ||
public Number Number { get; } = number; | ||
|
||
public abstract class EvenNumberCommand : NumberCommand | ||
public abstract class EvenNumberCommand(Description description, Number number) : NumberCommand(description, number) | ||
{ | ||
public sealed class AddEvenNumber(Description description, Number value) : EvenNumberCommand | ||
{ | ||
public override Description Description { get; } = description; | ||
public override Number Value { get; } = value; | ||
} | ||
public sealed class AddEvenNumber(Description description, Number number) | ||
: EvenNumberCommand(description, number); | ||
|
||
public sealed class SubtractEvenNumber(Description description, Number value) : EvenNumberCommand | ||
{ | ||
public override Description Description { get; } = description; | ||
public override Number Value { get; } = value; | ||
} | ||
public sealed class SubtractEvenNumber(Description description, Number number) | ||
: EvenNumberCommand(description, number); | ||
} | ||
|
||
public abstract class OddNumberCommand : NumberCommand | ||
public abstract class OddNumberCommand(Description description, Number number) : NumberCommand(description, number) | ||
{ | ||
public sealed class AddOddNumber(Description description, Number value) : OddNumberCommand | ||
{ | ||
public override Description Description { get; } = description; | ||
public override Number Value { get; } = value; | ||
} | ||
public sealed class AddOddNumber(Description description, Number number) : OddNumberCommand(description, number); | ||
|
||
public sealed class SubtractOddNumber(Description description, Number value) : OddNumberCommand | ||
{ | ||
public override Description Description { get; } = description; | ||
public override Number Value { get; } = value; | ||
} | ||
public sealed class SubtractOddNumber(Description description, Number number) | ||
: OddNumberCommand(description, number); | ||
} | ||
} |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
namespace Fraktalio.FModel.Tests.Examples.Numbers; | ||
|
||
public abstract record NumberState(Description Description, Number Value); | ||
public abstract record NumberState(Description Description, Number Value); | ||
|
||
public sealed record OddNumberState(Description Description, Number Value) : NumberState(Description, Value); | ||
|
||
public sealed record EvenNumberState(Description Description, Number Value) : NumberState(Description, Value); |
Oops, something went wrong.