Monad is very powerful tool for combining functions.
The definition of Monad(Kleisli triple) is following:
C : category
T : C → C : endofunctor
A, B : objects of category C
f : morphism of category C
u : A → TA
and
for any morphism f : A → TB, a morphism f' : TA → TB exists and satisfies:
1. u' = id
2. f = f'* u
3. f : A → TB, g : B → TC, (g' * f)' = g' * f'
I show the example of the validation for given value by C# code.
I prepare the following struct:
public struct Param<A>
{
private A value;
public A Value
{
get
{
if (!IsValid)
throws new Exception();
return this.value;
}
set { this.value = value; }
}
public bool IsValid { get; set; }
}
See Param as endfunctor T.
u : A → TA is defined as Unit : A → Param<A> :
Param<A> Unit(Param<A> a)
{
return new Param<A>() { Value = a, IsValid = true, };
}
f : A → TB i.e. Param<B> Validate(B b) { ... }
A function Validate2 : Param<A> → Param<B> , which is (Validate)' :
Param<B> Validate2(Param<A> pa)
{
return (pa.IsValid
? Validate(pa.Value)
: new Param<B>() { Value = default(B), IsValid = false, });
}
If we define the following function Extend, we obtain Validate2 as Extend(Validate).
Func<Param<A>, Param<B>> Extend(Func<A, Param<B>> f)
{
return (pa => (pa.IsValid
? f(pa.Value)
: new Param<B>() { Value = default(B), IsValid = false, }));
}
I confirm Extend(Unit),
Extend(Unit)(pa) =
return (pa => (pa.IsValid
? new Param<A>() { Value = pa.Value, IsValid = true, }
: new Param<A>() { Value = default(A), IsValid = false, }));
This result pa2 = Extend(Unit)(pa) satisfies :
if pa.IsValid then pa2.IsValid and pa2.Value == pa.Value,
if not pa.IsValid then not pa2.IsValid and pa2.Value throws Exception.
i.e. Extend(Unit) is Id.
For ValidateX : A → TB and ValidateY : B → TC,
Extend(Extend(ValidateY)(ValidateX)) =
return (pa => ((Extend(ValidateY)(ValidateX))(pa)).IsValid
? (Extend(ValidateY)(ValidateX))(pa)
: new Param<C>() { Value = default(C), IsValid = false, }));
=
return (pa => ((Extend(ValidateY)(Extend(ValidateX)(pa))).IsValid
? (Extend(ValidateY)(Extend(ValidateX)(pa))
: new Param<C>() { Value = default(C), IsValid = false, }));
=
Extend(ValidateY)(Extend(ValidateX))
These are equal.