Monad<Env, Out, MA, A> Interface

Monad type-class

DECLARATION
[Typeclass]
public interface Monad<Env, Out, MA, A> : Foldable<Env, MA, A>,
FoldableAsync<Env, MA, A>
NAMESPACE
LanguageExt.TypeClasses

Methods

Apply(Func<A, A, A>, MA, MA)
SUMMARY

Apply - used to facilitate default behavior for monad transformers NOTE: Don't rely on this, it may not be a permanent addition to the project

PARAMETERS
faac
Func<A, A, A>
fa
MA
fb
MA
DECLARATION
[Pure]
private MA Apply(Func<A, A, A> faac, MA fa, MA fb)
RETURNS
MA
Bind<MONADB, MB, B>(MA, Func<A, MB>)
SUMMARY

Monadic bind

PARAMETERS
ma
MA
Monad to bind
f
Func<A, MB>
Bind function
DECLARATION
[Pure]
private MB Bind<MONADB, MB, B>(MA ma, Func<A, MB> f)
RETURNS
MB
Monad of type MB derived from Monad of B
CONSTRAINTS
where MONADB : struct Monad<Env, Out, MB, B>
BindReturn(Out, MA)
SUMMARY

Used for double dispatch by the bind function for monadic types that need to construct an output value/state (like MState and MWriter). For all other monad types just return mb.

PARAMETERS
outputma
Out
Output from the first part of a monadic bind
mb
MA
Monadic to invoke. Get the results from this to combine with outputma and then re-wrap
REMARKS
This is an example from the Writer monad. Note how the Output argument for the Writer monad is (W, bool). W is what the Writer tells, and bool is a flag stating whether it's faulted or not. The Bind function isn't able to combine the output from ma and mb, due to limitations in the type-system's ability to explicitly constrain the return type of Bind to be a Writer monad; the returned monad of MB could be any monad, but it must be compatible with the input type of Unit and the output type of (W, bool). It restricts MB to be:

Monad<Unit, (W, bool), MB, B>


So the Writer's Bind function calls BindReturn which double dispatches the job to the BindReturn function on MONADB. This is very much like the Visitor pattern in OO land.

public MB Bind<MONADB, MB, B>(Writer<MonoidW, W, A> ma, Func<A, MB> f)
where MONADB : struct, Monad<Unit, (W, bool), MB, B> =>
default(MONADB).Id(_ =>
{
var(a, output1, faulted) = ma();
return faulted
? default(MONADB).Fail()
: default(MONADB).BindReturn((output1, faulted), f(a));
});


Usually MONADB would be another Writer instance, because you would normally bind a Writer with a Writer, but it could be any monad that has the same input and output arguments. The BindReturn function is then able to invoke mb, because it knows its own context and combine the output from ma() and the output of mb.

public Writer<MonoidW, W, A> BindReturn((W, bool) output, Writer<MonoidW, W, A> mb)
{
var (b, output2, faulted) = mb();
return () => faulted
? (default(A), default(MonoidW).Empty(), true)
: (b, default(MonoidW).Append(output.Item1, output2), false);
}


The effect of this with the monadic types in Language-Ext is that Writers are only bindable to Writers. However simpler monads like Option can be bound to Either, Try, etc. Because their BindReturn function looks like this:

public Option<A> BindReturn(Unit _, Option<A> mb) =>
mb;


The Bind function for Option doesn't call BindReturn at all:

public MB Bind<MONADB, MB, B>(Option<A> ma, Func<A, MB> f)
where MONADB : struct, Monad<Unit, Unit, MB, B> =>
ma.IsSome && f != null
? f(ma.Value)
: default(MONADB).Fail();


So why implement it? If someone tries to return an Option from a Bind call with the source monad of another type, it may call BindReturn. And the Option respose would be to just return itself.

So Bind and BindReturn should be seen as two halves of the same function. They're there to make use of the instances knowledge about itself, but not its generic return types.
DECLARATION
private MA BindReturn(Out outputma, MA mb)
RETURNS
MA
Monad with the combined output
Fail(object)
SUMMARY

Produce a monad of MA in it's failed state

PARAMETERS
err
object
DECLARATION
[Pure]
private MA Fail(object err = null)
RETURNS
MA
Id(Func<Env, MA>)
SUMMARY

The Id function allows the Bind function to construct a monad from a function rather than MA. It's a form of double-dispatch like the BindReturn function. It hands context to the type that knows how to construct. This facilitates the unification of Monads that take arguments (like Reader, State, etc.) with ones that don't (Option, Try, Writer, Lst, Either, etc.)

PARAMETERS
ma
Func<Env, MA>
REMARKS
For monads that don't take arguments, they will have an input type of Unit. And so implementing Id is as simple as (for Option<A>):

public Option<A> Id(Func<Unit, Option<A>> ma) =>
ma(unit);


The most complex example is the State monad. It takes a type S which is the input state:

public State<S, A> Id(Func<S, State<S, A>> ma) =>
state => ma(state)(state);


That appears to be ignoring the return state of ma(state), but if you look at the Bind and BindReturn functions for MState:

public MB Bind<MONADB, MB, B>(State<S, A> ma, Func<A, MB> f)
where MONADB : struct, Monad<S, (S State, bool IsFaulted), MB, B> =>
default(MONADB).Id(state =>
{
var (a, sa, faulted) = ma(state);
return faulted
? default(MONADB).Fail()
: default(MONADB).BindReturn((sa, faulted), f(a));
});


public State<S, A> BindReturn((S State, bool IsFaulted) output, State<S, A> mb) =>
_ => mb(output.State);


It should be clear that Id accepts the state (the first state in ma(state)(state)), and it's result is the return value of BindReturn which ignores its incoming state so that it can bind the output of the call to ma(state) in the Bind function.

Simple monads that don't take parameters simply ignore this in thier Bind functions:

public MB Bind<MONADB, MB, B>(Option<A> ma, Func<A, MB> f)
where MONADB : struct, Monad<Unit, Unit, MB, B> =>
ma.IsSome && f != null
? f(ma.Value)
: default(MONADB).Fail();


The Id function would allow two monads of different types to be bound as long as their input and output types are the same.
DECLARATION
private MA Id(Func<Env, MA> ma)
RETURNS
MA
IdAsync(Func<Env, Task<MA>>)
SUMMARY

The IdAsync function allows the Bind function to asynchronously construct a monad from a function that returns a Task<MA> rather than MA. It's a form of double-dispatch like the BindReturn function. It hands context to the type that knows how to construct. This facilitates the unification of Monads that are asynchronous in nature (like TryAsync, TryOptionAsync, Task) with ones that aren't (State, Reader, Writer, Option, Try, Lst, Either, etc.)

PARAMETERS
ma
Func<Env, Task<MA>>
REMARKS
This is the async version of Id(ma). It allows the asynchronous type to return without waiting for a result. For example, TryAsync:

public TryAsync<A> IdAsync(Func<Unit, Task<TryAsync<A>>> ma) =>
new TryAsync<A>(() =>
from a in ma(unit)
let b = a()
from c in b
select c);


The LINQ expression is using the Task<A> Select and SelectMany overloads. So the result is asynchronous.

If you look at IdAsync for Option, which is not an asynchronous type, you'll see it waits for the Result:

public Option<A> IdAsync(Func<Unit, Task<Option<A>>> ma) =>
ma(unit).Result;


That means you can bind asynchronous and synchronous monads together, but the result will be synchronous.
DECLARATION
private MA IdAsync(Func<Env, Task<MA>> ma)
RETURNS
MA
Plus(MA, MA)
SUMMARY

Associative binary operation

PARAMETERS
a
MA
b
MA
DECLARATION
[Pure]
private MA Plus(MA a, MA b)
RETURNS
MA
Return(Func<Env, A>)
SUMMARY

Lazy monad constructor function. Provide the bound value A to construct a new monad of type MA. This varies from the 'standard' construction of monadic types in that it takes an input parameter. This function allows monads that take parameters (like Reader and State) to be unified with non-parametric monads like (Option, Either, etc.), which take Unit as their input argument.

PARAMETERS
f
Func<Env, A>
REMARKS
Any instance of this interface must be a struct to use the Return function effectively. Then the instance can be used as a generic argument constrained to:

where MonadA : struct, Monad<Env, Out, MA, A>


And any consumer of the argument can call:

MA monad = default(MonadA).Return(a);


To construct a monad of type MA.
DECLARATION
private MA Return(Func<Env, A> f)
RETURNS
MA
Monad of type MA
Zero()
SUMMARY

Neutral element (None in Option for example)

DECLARATION
[Pure]
private MA Zero()
RETURNS
MA