-
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
4 changed files
with
345 additions
and
0 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,14 @@ | ||
namespace Fraktalio.FModel.Contracts; | ||
|
||
/// <summary> | ||
/// Decider Interface | ||
/// </summary> | ||
/// <typeparam name="C">C Command</typeparam> | ||
/// <typeparam name="S">S State</typeparam> | ||
/// <typeparam name="E">E Event</typeparam> | ||
public interface IDecider<in C, S, E> | ||
{ | ||
Func<C, S, IEnumerable<E>> Decide { get; } | ||
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,65 @@ | ||
using Fraktalio.FModel.Contracts; | ||
|
||
namespace Fraktalio.FModel; | ||
|
||
/// <summary> | ||
/// [Decider] is a datatype that represents the main decision-making algorithm. | ||
/// | ||
/// It has three generic parameters `C`, `S`, `E` , representing the type of the values that [Decider] may contain or use. | ||
/// [Decider] can be specialized for any type `C` or `S` or `E` because these types does not affect its behavior. | ||
/// [Decider] behaves the same for `C`=[Int] or `C`=`OddNumberCommand`. | ||
/// </summary> | ||
/// <param name="decide">A function/lambda that takes command of type [C] and input state of type [S] as parameters, and returns/emits the list of output events [E]</param> | ||
/// <param name="evolve">A 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">A starting point / An initial state of type [S]</param> | ||
/// <typeparam name="C">Command</typeparam> | ||
/// <typeparam name="S">State</typeparam> | ||
/// <typeparam name="E">Event</typeparam> | ||
public class Decider<C, S, E>(Func<C, S, IEnumerable<E>> decide, Func<S, E, S> evolve, S initialState) | ||
: IDecider<C, S, E> | ||
{ | ||
public Func<C, S, IEnumerable<E>> Decide { get; } = decide; | ||
public Func<S, E, S> Evolve { get; } = evolve; | ||
public S InitialState { get; } = initialState; | ||
|
||
/// <summary> | ||
/// Left map on C/Command parameter - Contravariant | ||
/// </summary> | ||
/// <param name="f"></param> | ||
/// <typeparam name="Cn"></typeparam> | ||
/// <returns></returns> | ||
public Decider<Cn, S, E> MapLeftOnCommand<Cn>(Func<Cn, C> f) | ||
{ | ||
var internalDecider = new InternalDecider<C, S, S, E, E>(Decide, Evolve, InitialState); | ||
var mappedInternalDecider = internalDecider.MapLeftOnCommand(f); | ||
return mappedInternalDecider.AsDecider(); | ||
} | ||
|
||
/// <summary> | ||
/// Di-map on E/Event parameter | ||
/// </summary> | ||
/// <param name="fl"></param> | ||
/// <param name="fr"></param> | ||
/// <typeparam name="En"></typeparam> | ||
/// <returns></returns> | ||
public Decider<C, S, En> DimapOnEvent<En>(Func<En, E> fl, Func<E, En> fr) | ||
{ | ||
var internalDecider = new InternalDecider<C, S, S, E, E>(Decide, Evolve, InitialState); | ||
var mappedInternalDecider = internalDecider.DimapOnEvent(fl, fr); | ||
return mappedInternalDecider.AsDecider(); | ||
} | ||
|
||
/// <summary> | ||
/// Di-map on S/State parameter | ||
/// </summary> | ||
/// <param name="fl"></param> | ||
/// <param name="fr"></param> | ||
/// <typeparam name="Sn"></typeparam> | ||
/// <returns></returns> | ||
public Decider<C, Sn, E> DimapOnState<Sn>(Func<Sn, S> fl, Func<S, Sn> fr) | ||
{ | ||
var internalDecider = new InternalDecider<C, S, S, E, E>(Decide, Evolve, InitialState); | ||
var mappedInternalDecider = internalDecider.DimapOnState(fl, fr); | ||
return mappedInternalDecider.AsDecider(); | ||
} | ||
} |
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,37 @@ | ||
namespace Fraktalio.FModel; | ||
|
||
public static class DeciderExtensions | ||
{ | ||
/// <summary> | ||
/// Combine [Decider]s into one [Decider] | ||
/// | ||
/// Possible to use when: | ||
/// - [E] and [E2] have common superclass [E_SUPER] | ||
/// - [C] and [C2] have common superclass [C_SUPER] | ||
/// </summary> | ||
/// <param name="x">First decider</param> | ||
/// <param name="y">Second decider</param> | ||
/// <typeparam name="C">Command type of the first Decider</typeparam> | ||
/// <typeparam name="S">State type of the first Decider</typeparam> | ||
/// <typeparam name="E">Event type of the first Decider</typeparam> | ||
/// <typeparam name="C2">Command type of the second Decider</typeparam> | ||
/// <typeparam name="S2">Input_State type of the second Decider</typeparam> | ||
/// <typeparam name="E2">Event type of the second Decider</typeparam> | ||
/// <typeparam name="C_SUPER">super type of the command types C and C2</typeparam> | ||
/// <typeparam name="E_SUPER">super type of the E and E2 types</typeparam> | ||
/// <returns>Combined decider</returns> | ||
public static Decider<C_SUPER?, Tuple<S, S2>, E_SUPER?> Combine<C, S, E, C2, S2, E2, C_SUPER, E_SUPER>( | ||
this Decider<C?, S, E?> x, Decider<C2?, S2, E2?> y) | ||
where C : class, C_SUPER | ||
where C2 : class, C_SUPER | ||
where E : class, E_SUPER | ||
where E2 : class, E_SUPER | ||
{ | ||
var internalDeciderX = new InternalDecider<C?, S, S, E?, E?>(x.Decide, x.Evolve, x.InitialState); | ||
var internalDeciderY = new InternalDecider<C2?, S2, S2, E2?, E2?>(y.Decide, y.Evolve, y.InitialState); | ||
var combinedInternalDecider = | ||
internalDeciderX.Combine<C?, S, S, E?, E?, C2, S2, S2, E2, E2, C_SUPER, E_SUPER?, E_SUPER?>( | ||
internalDeciderY); | ||
return combinedInternalDecider.AsDecider(); | ||
} | ||
} |
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,229 @@ | ||
namespace Fraktalio.FModel; | ||
|
||
|
||
|
||
/// <summary> | ||
/// [InternalDecider] is a datatype that represents the main decision-making algorithm. | ||
/// It has five generic parameters [C], [Si], [So], [Ei], [Eo] , representing the type of the values that [InternalDecider] may contain or use. | ||
/// [InternalDecider] can be specialized for any type [C] or [Si] or [So] or [Ei] or [Eo] because these types does not affect its behavior. | ||
/// [InternalDecider] behaves the same for [C]=[Int] or [C]=YourCustomType, for example. | ||
/// | ||
/// [InternalDecider] is a pure domain component. | ||
/// </summary> | ||
/// <typeparam name="C">C Command type</typeparam> | ||
/// <typeparam name="Si">Si Input State type</typeparam> | ||
/// <typeparam name="So">Output State type</typeparam> | ||
/// <typeparam name="Ei">Input Event type</typeparam> | ||
/// <typeparam name="Eo">Output Event type</typeparam> | ||
internal class InternalDecider<C, Si, So, Ei, Eo> | ||
{ | ||
/// <summary> | ||
/// [InternalDecider] is a datatype that represents the main decision-making algorithm. | ||
/// It has five generic parameters [C], [Si], [So], [Ei], [Eo] , representing the type of the values that [InternalDecider] may contain or use. | ||
/// [InternalDecider] can be specialized for any type [C] or [Si] or [So] or [Ei] or [Eo] because these types does not affect its behavior. | ||
/// [InternalDecider] behaves the same for [C]=[Int] or [C]=YourCustomType, for example. | ||
/// | ||
/// [InternalDecider] is a pure domain component. | ||
/// </summary> | ||
/// <typeparam name="C">C Command type</typeparam> | ||
/// <typeparam name="Si">Si Input State type</typeparam> | ||
/// <typeparam name="So">Output State type</typeparam> | ||
/// <typeparam name="Ei">Input Event type</typeparam> | ||
/// <typeparam name="Eo">Output Event type</typeparam> | ||
internal InternalDecider(Func<C, Si, IEnumerable<Eo>> decide, | ||
Func<Si, Ei, So> evolve, | ||
So initialState) | ||
{ | ||
Decide = decide; | ||
Evolve = evolve; | ||
InitialState = initialState; | ||
} | ||
|
||
/// <summary> | ||
/// A function/lambda that takes command of type [C] and input state of type [Si] as parameters, and returns/emits the list of output events | ||
/// </summary> | ||
public Func<C, Si, IEnumerable<Eo>> Decide { get; } | ||
|
||
/// <summary> | ||
/// A function/lambda that takes input state of type [Si] and input event of type [Ei] as parameters, and returns the output/new state [So] | ||
/// </summary> | ||
public Func<Si, Ei, So> Evolve { get; } | ||
|
||
/// <summary> | ||
/// A starting point / An initial state of type [So] | ||
/// </summary> | ||
public So InitialState { get; } | ||
|
||
/// <summary> | ||
/// Left map on C/Command parameter - Contravariant | ||
/// </summary> | ||
/// <param name="f"></param> | ||
/// <typeparam name="Cn">Cn Command new</typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<Cn, Si, So, Ei, Eo> MapLeftOnCommand<Cn>(Func<Cn, C> f) | ||
{ | ||
return new InternalDecider<Cn, Si, So, Ei, Eo>( | ||
(cn, si) => Decide(f(cn), si), | ||
(si, ei) => Evolve(si, ei), | ||
InitialState | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Dimap on E/Event parameter - Contravariant on input event and Covariant on output event = Profunctor | ||
/// </summary> | ||
/// <param name="fl"></param> | ||
/// <param name="fr"></param> | ||
/// <typeparam name="Ein">Event input new</typeparam> | ||
/// <typeparam name="Eon">Event output new</typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<C, Si, So, Ein, Eon> DimapOnEvent<Ein, Eon>(Func<Ein, Ei> fl, Func<Eo, Eon> fr) | ||
{ | ||
return new InternalDecider<C, Si, So, Ein, Eon>( | ||
(c, si) => Decide(c, si).Select(fr), | ||
(si, ein) => Evolve(si, fl(ein)), | ||
InitialState | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Left map on E/Event parameter - Contravariant | ||
/// </summary> | ||
/// <param name="f"></param> | ||
/// <typeparam name="Ein"></typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<C, Si, So, Ein, Eo> MapLeftOnEvent<Ein>(Func<Ein, Ei> f) | ||
{ | ||
return DimapOnEvent<Ein, Eo>(f, eo => eo); | ||
} | ||
|
||
/// <summary> | ||
/// Right map on E/Event parameter - Covariant | ||
/// </summary> | ||
/// <param name="f"></param> | ||
/// <typeparam name="Eon"></typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<C, Si, So, Ei, Eon> MapOnEvent<Eon>(Func<Eo, Eon> f) | ||
{ | ||
return DimapOnEvent<Ei, Eon>(ei => ei, f); | ||
} | ||
|
||
/// <summary> | ||
/// Dimap on S/State parameter - Contravariant on input state (Si) and Covariant on output state (So) = Profunctor | ||
/// </summary> | ||
/// <param name="fl"></param> | ||
/// <param name="fr"></param> | ||
/// <typeparam name="Sin">State input new</typeparam> | ||
/// <typeparam name="Son">State output new</typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<C, Sin, Son, Ei, Eo> DimapOnState<Sin, Son>(Func<Sin, Si> fl, Func<So, Son> fr) | ||
{ | ||
return new InternalDecider<C, Sin, Son, Ei, Eo>( | ||
(c, sin) => Decide(c, fl(sin)), | ||
(sin, ei) => fr(Evolve(fl(sin), ei)), | ||
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 InternalDecider<C, Sin, So, Ei, Eo> MapLeftOnState<Sin>(Func<Sin, Si> f) | ||
{ | ||
return DimapOnState<Sin, So>(f, so => so); | ||
} | ||
|
||
/// <summary> | ||
/// Right map on S/State parameter - Covariant | ||
/// </summary> | ||
/// <param name="f"></param> | ||
/// <typeparam name="Son"></typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<C, Si, Son, Ei, Eo> MapOnState<Son>(Func<So, Son> f) | ||
{ | ||
return DimapOnState<Si, Son>(si => si, f); | ||
} | ||
|
||
/// <summary> | ||
/// Apply on S/State - Applicative | ||
/// </summary> | ||
/// <param name="ff"></param> | ||
/// <typeparam name="Son"></typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<C, Si, Son, Ei, Eo> ApplyOnState<Son>(InternalDecider<C, Si, Func<So, Son>, Ei, Eo> ff) | ||
{ | ||
return new InternalDecider<C, Si, Son, Ei, Eo>( | ||
(c, si) => ff.Decide(c, si).Concat(Decide(c, si)), | ||
(si, ei) => ff.Evolve(si, ei)(Evolve(si, ei)), | ||
ff.InitialState(InitialState) | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Product on S/State parameter - Applicative | ||
/// </summary> | ||
/// <param name="fb"></param> | ||
/// <typeparam name="Son"></typeparam> | ||
/// <returns></returns> | ||
public InternalDecider<C, Si, Tuple<So, Son>, Ei, Eo> ProductOnState<Son>(InternalDecider<C, Si, Son, Ei, Eo> fb) | ||
{ | ||
return ApplyOnState(fb.MapOnState(b => new Func<So, Tuple<So, Son>>(a => new Tuple<So, Son>(a, b)))); | ||
} | ||
} | ||
|
||
internal static class InternalDeciderExtensions | ||
{ | ||
/// <summary> | ||
/// Combine [InternalDecider]s into one big [InternalDecider] | ||
/// | ||
/// Possible to use when: | ||
/// - [Ei] and [Ei2] have common superclass [Ei_SUPER] | ||
/// - [Eo] and [Eo2] have common superclass [Eo_SUPER] | ||
/// - [C] and [C2] have common superclass [C_SUPER] | ||
/// </summary> | ||
/// <param name="x">First Decider</param> | ||
/// <param name="y">Second Decider</param> | ||
/// <typeparam name="C">Command type of the first Decider</typeparam> | ||
/// <typeparam name="Si">Input_State type of the first Decider</typeparam> | ||
/// <typeparam name="So">Output_State type of the first Decider</typeparam> | ||
/// <typeparam name="Ei">Input_Event type of the first Decider</typeparam> | ||
/// <typeparam name="Eo">Output_Event type of the first Decider</typeparam> | ||
/// <typeparam name="C2">Command type of the second Decider</typeparam> | ||
/// <typeparam name="Si2">Input_State type of the second Decider</typeparam> | ||
/// <typeparam name="So2">Output_State type of the second Decider</typeparam> | ||
/// <typeparam name="Ei2">Input_Event type of the second Decider</typeparam> | ||
/// <typeparam name="Eo2">Output_Event type of the second Decider</typeparam> | ||
/// <typeparam name="C_SUPER">super type of the command types C and C2</typeparam> | ||
/// <typeparam name="Ei_SUPER">Super type of the Ei and Ei2 types</typeparam> | ||
/// <typeparam name="Eo_SUPER">super type of the Eo and Eo2 types</typeparam> | ||
/// <returns></returns> | ||
public static InternalDecider<C_SUPER?, Tuple<Si, Si2>, Tuple<So, So2>, Ei_SUPER, Eo_SUPER?> Combine<C, Si, So, Ei, | ||
Eo, C2, Si2, So2, Ei2, Eo2, C_SUPER, Ei_SUPER, Eo_SUPER>( | ||
this InternalDecider<C?, Si, So, Ei?, Eo?> x, | ||
InternalDecider<C2?, Si2, So2, Ei2?, Eo2?> y) | ||
where C : class?, C_SUPER? | ||
where C2 : class?, C_SUPER | ||
where Ei : Ei_SUPER | ||
where Eo : Eo_SUPER | ||
where Ei2 : Ei_SUPER | ||
where Eo2 : Eo_SUPER | ||
{ | ||
var deciderX = x.MapLeftOnCommand<C_SUPER?>(c => (c as C)!) | ||
.MapLeftOnState<Tuple<Si, Si2>>(pair => pair.Item1) | ||
.DimapOnEvent<Ei_SUPER, Eo_SUPER?>(ei => (Ei)ei!, eo => eo); | ||
|
||
var deciderY = y.MapLeftOnCommand<C_SUPER?>(c => (c as C2)!) | ||
.MapLeftOnState<Tuple<Si, Si2>>(pair => pair.Item2) | ||
.DimapOnEvent<Ei_SUPER, Eo_SUPER?>(ei => (Ei2)ei!, eo => eo); | ||
|
||
return deciderX.ProductOnState(deciderY); | ||
} | ||
|
||
internal static Decider<C, S, E> AsDecider<C, S, E>(this InternalDecider<C, S, S, E, E> internalDecider) | ||
{ | ||
return new Decider<C, S, E>(internalDecider.Decide, internalDecider.Evolve, internalDecider.InitialState); | ||
} | ||
} |