Monad type-class

DECLARATION

[Typeclass]

public interface Monad<Env, Out, MA, A> :
Foldable<Env, MA, A>,FoldableAsync<Env, MA, A>

NAMESPACE

LanguageExt.TypeClassesSUMMARY

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

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>

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

SUMMARY

Produce a monad of MA in it's failed state

PARAMETERS

err

object

DECLARATION

[Pure]

private MA Fail(object err = null)RETURNS

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

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

SUMMARY

Associative binary operation

PARAMETERS

a

MA

b

MA

DECLARATION

[Pure]

private MA Plus(MA a, MA b)RETURNS

MA

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

SUMMARY

Neutral element (None in Option for example)

DECLARATION

[Pure]

private MA Zero()RETURNS

MA