- Details
- Category: Code
- By Stuart Mathews
- Hits: 2805
Here is an example of some code that is designed to be functional, that is, its designed to enforce immutability and restrict the creation of exceptions - which can change the outcome of functions unpredictably.
There is a lot to be said about this code, including a mysterious Option<T> class from the Language.Ext project but I'll do that next time. For now just look at how it's trying to achieve immutability, mostly around making new copies when its asked to modify itself. Enjoy!
namespace FunctionalProgramming
{
/// <summary>
/// This is an immutable object because:
/// a) Has private setters and that means its state cannot be changed after creation
/// b) All properties are immutable - either strings or native types or System.Collections.Immutable types
/// c) Any change that needs to take place must result in a newly create object of this type
/// </summary>
public class Person
{
public string FirstName { get; }
public string LastName { get; }
/// <summary>
/// Notice this is a private constructor so a Person cannot be created normally...there must be another way in!
/// It must be created indirectly via .Of() or .New() which then can use the private constructor below
/// </summary>
/// <param name="firstName">the persons first name</param>
/// <param name="lastName">The persons last name</param>
private Person(string firstName, string lastName)
{
if (!IsValid(firstName, lastName))
{
throw new ArgumentException("Invalid input");
}
FirstName = firstName;
LastName = lastName;
}
/// <summary>
/// This pretends to be a constructor by being the only method allowed to call the constructor.
/// The actual and real constructor is only called if input is valid to this function
/// As a result it gaurentees no exceptions can ever be thrown during creation of the object through
/// this method.
/// </summary>
/// <param name="firstName">Persons first name</param>
/// <param name="lastName">Persons last name</param>
/// <returns></returns>
public static Option<Person> Of(string firstName, string lastName)
=> IsValid(firstName, lastName)
? Option <Person>.Some(new Person(firstName, lastName))
: Option <Person>.None;
private static bool IsValid(string firstName, string lastName)
=> string.IsNullOrEmpty(firstName) && string.IsNullOrEmpty(lastName);
// Instance method allows this function to be chained.
// With Helper to make object creation easier as properties of the object change
public Person With(string firstName = null, string lastName = null)
{
return new Person(firstName ?? this.FirstName, lastName ?? this.LastName);
}
}
// Extension methods allow us to chain non-instance method calls,
public static class PersonExtensions
{
/// <summary>
/// Functional because
/// A) it does not call any non-pure functions or use any other data than the arguments(with are immutable) passed to it.
/// B) functions should be declared separately from the data they act upon, like it is
/// C) Creates a new Object
/// </summary>
/// <param name="person"></param>
/// <param name="firstName"></param>
/// <returns></returns>
public static Person Rename(this Person person, string firstName)
{
return person.With(firstName: firstName);
}
}
}
The source for this is here: https://github.com/stumathews/FunctionalProgrammingTests/blob/master/FunctionalProgramming/Program.cs
- Details
- Category: Code
- By Stuart Mathews
- Hits: 3367
A Container is IEnumerable<T> or Either<T1,T2> or loosely any type that contains other things...
The Select() and SelectMany() functions in Linq all extract/iterate an item-at-a-time from the provided container before proceeding further.
It runs a user provided function on it each extracted item and puts the result into a new container. If no items, it puts nothing into the container and a empty container is produced
The function is either a non-container producing function, usually named map() or a container-producing function usually called bind(). Both functions transform their input so effectively apart from how they return it, they do the same type of job - transformation.
The result of the scalar function is put into a new container either automatically by the Select/Map() functions for explicitly if you use Bind().
The result of the monad-producing function ie bind() is further extracted and that's put in the new container. If no result, nothing or None/Bad/ ndicating value put int Container
Short story:
-
Extract item from container,
-
Inspect the item to see it exists or is valid
-
If so(valid):
-
if scalar function, execute scalar function and return scalar result into new container,
-
if monad-producing function ie bind(), execute it and if any items in continer extract items from container and put into new container.
-
-
An item extracted from an IEnumerable<T> is extracted via the foreach Operator.
-
An item extracted from an Option<T> is extracted via Match() or Linq Select synatax
Eg:
Bind function attempts to run a monad-producing function on each item it can extract from the container. If it cannot extract the item, it doesn't run the function. Each item is extracted from that monad-producing function ie bind and returned.
public static Option<TOUT> Bind<TIN,TOUT>( this Option<TIN> self, // Container
Func<TIN, Option<OUT> bind // Container producing function
)
=> self.Match( // A) extract item from container
// and check its validity...
Some: a => bind(a), // B) If item valid (in this case just being
// a SOME makes it valid), run container-producing function
// bind() on item which will return another Container
None: () => None // If could not extract item or the extracted item
// is not valid, return Container of None
)
For Either<L,R>:
/* Bind runs a container producing function */
public static Either<L,R2> Bind<L, R1>( this Either<L,R1> self, // Takes in a container of Either<L, R1>
Func<R1, Either<L,R2> bind // Takes a container producing function
)
=> self.Match( // extract item from container
// and verify item
Right: r => bind(r), // if its a right item, its OK, run the container-producing function
// that in turn returns a container for next bind() if there is one...
Left: () => Left // if its a left, don't do container-producing function, just return left again..
)
Each time a Bind() encounters a Left in the container, it returns a left. If the container, in this case, is an instance of Either<Left, Right> but contains a left value:
result = Container.Bind(me_not_run()) // Container determined to not have right(correct), so return LEFT
Container.Bind(me_not_run_either()) // Container determined to be left(wrong), so return LEFT
Container.Bind(me_not_run_neither()) // Container determined to be left(wrong), so return LEFT
And Consequently never runs the user container producing function in this case…the resulting container is just a container with a Left in it that cascades all the way down and ultimately the result is a Container of Left.
Container1
// Attempt to extract item from Container1 and run container-producing function
// on it. Return the extracted item from container or
// indicate NotFound be returning Left
.Bind( item => { /* container producing function which return Container.
internally if extracted, returns each member of container otherwise Returns
Container<None/Left> /* }
.Bind( item => { ret Container} ) // Else Container<None/Left/Blank>
// If Has Item Of required (any Right)
.Bind( item => { do something on it and return Container} // else Container<None/Left>
// Whats hidden if HOW we extract the item, and what container to return
// if the item was not found or correct. Otherwise just return items
// from container produced from container-producing function
Bind's Story:
If the Container has the required value(or simple criteria of any specific value) then Get it and run a container-producing function on it.
If the Container doesn't, don't run the function and return a Blank/NotFound Container.
Plan your conditional code like this:
if(ContainerHasOKItem) {
// Your Ok Code which works with the OK Item and produces another Container
// for next Bind() to interrogate for OKness.
} else {
// Internal/Hidden code returns Container<BAD> for next Bind() to integorate for OKness.
}
which corresponds to the functional notation of:
Container.Bind(item=> me) // if my container has a valid item or state, run me()
// on the item or state. me() will return a container
.Bind(item => me) // this is the next bind which will re-check the
// container for a valid item or state etc..
.Bind(item => me)
// Internally Bind() checks if the container has Good or Bad value in it,if it has
// bad it returns Container<Bad> otherwise if returns your
// container producing function (which could container a good or
// bad value based on if your function said it should)
ContainerA
.Bind(containerA_has_ok_so_run_my_ok_func) // Produces a Container, Bind() then
// inspects it - returns Container<Bad> or passed Good to my_func
.Bind(containerA_has_ok_so_run_my_ok_func2) // Produces a Container ...
.Bind(containerA_has_ok_so_run_my_ok_func3) // Produces a Container ...
Note: The determination of what is a required value for the container to have, for the monad-producing() function to be called is within specified/coded within the Bind extension method.
So with all this being said the underlying ideas are essentially this:
-
Extract an item from the container
-
Verify the extracted item or state of container is suitable and run monad-producing function on it.
-
If the container or the item extracted from it is undesirable then return Container indicating bad item/state.
Something like this hopefully demonstrates it:
How much does your brain hurt now?
Side notes:
What makes a Monad a Monad?
Option<T> captures if behavior IEnumerable<T> captures foreach behavior Select/Map make types into functors Functors allow you to use regular functions(Fn()) with types that contain or amplify other types (T<A>): T<A>.Map(Fn()) Map is a Select which is a way to remove the boilder place code of a foreach Bind is a SelectMany without the project function. Bind() is a function that does the binding of one Monad to another
- Details
- Category: Code
- By Stuart Mathews
- Hits: 12335
Lets talk about Language.Ext specifically and if you're interested in the languageExt tutorial go to The languageExt Tutorial
If you want to know a little about functional programming ideas generally, checkout
People who know Functional programming tend to throw around a lot of buzzwords like "Composing of functions", "Declarative code syntax", "Monads", "Functors", "Pure functions", "Immutability". The former I talked about in Pure functions, and the later, I talked about in Being a bit more functional
So lets talk about monads.
Monads are design patterns designed to prevent boilerplate code.
Enumerable<T> is a monad that removes the foreach boilerplate code. Monads can also eliminate the need to if..else imperative conditionals in your code but still achieve them. Monads can use Bind() to achieve this. I'll talk about how that looks like a bit later.
So what are monads? Well, they are just classes or objects really. Usually, they are likened to C++ STL containers in as much as they need to support a specific interface and have features that make working with any monads predictable. For now, you can assume a monad is just a type like a stack or a list that have the following distinct features:
Monads are objects that:
-
Have a Select() extension method extracts an item from the Monad and allows a function to run for each item in the monad. That function happens to be called a mapping function (a value producing function). You can safely assume that a monad is a container of items or just one item and you need to access the elements of a container. A mapping function allows you to not only access the items but act on each item or transform it someway). This is what a user-provided mapping function will do for a monad to which its used on. By applying a function that runs against each item in the monad and turns in into something else.
-
Have a SelectMany()-like extension method (its called Bind() ) which extracts an item from the Monad that allows a monad producing a function to be run for each item in the monad. That function happens to be called a binding function (a monad producing function)
-
This is not SelectMany() and described in Understanding how the Linq query syntax works but similar as it doesn't actually call any project() and its called Bind()!
-
- Have the following functions defined for the type:
-
Sum // For Option<int> it's the wrapped value. Count // For Option<T> is always 1 for Some and 0 for None. Bind // Part of the definition of anything monadic - SelectMany in LINQ Exists // Any in LINQ - true if any element fits a predicate Filter // Where in LINQ Fold // Aggregate in LINQ ForAll // All in LINQ - true if all element(s) fits a predicate Iter // Passes the wrapped value(s) to an Action delegate Map // Part of the definition of any 'functor'. Select in LINQ Lift / LiftUnsafe // Different meaning to Haskell, this returns the wrapped value. Dangerous, should be used sparingly. Select SeletMany Where
It must just be quickly be said, that the Select() function on a Monad or on an IEnumerable<T> fulfils the same functionality as the foreach operator and as such removes the need to use it and thus removes that particular piece of boilerplate code. The IF conditional will be replaced by the Bind() operation on Monads but I'm getting ahead of myself.
This enables Linq syntax (which is usable only by types that have a Select() and SellectMany()) to work on monads (Monads do have a SelectMany() and a Bind() also).
To understand how Select() and SelectMany() works checkout Understanding how the Linq query syntax works. There i describe how IEnumerable has Select() and SelectMany().
This suggests that you can enable any types (much as monads) to be used in LINQ expressions if they have a Select() and a SelectMany() like I've done here
But while your types can be used in LINQ expressions these types are not yet Monads until they also have Return() and Bind() extension methods (which are also similar to their Select and SelectMany counterparts):
-
Map() extension method, like Select extension method, extracts the item and runs a provided mapping function on it, however, it will put the transformation into a monad
-
Bind() extension method, like the SelectMany extension method, extracts the item and passes it to a binding function(monad producing function) without the project() function that SelectMany() performs. The transfomation function needs to return a monad ie put the transformation into a monad.
This is how Linq is used, just it with an IEnumerable<T> to deal with a Monad called an Option<T> in language.ext:
using static LanguageExt.Prelude;
Option<string> optionA = Some("Hello, ");
Option optionB = Some("World");
Option optionNone = None;
var result1 = from x in optionA
from y in optionB
select x + y;
var result2 = from x in optionA
from y in optionB
from z in optionNone
select x + y + z;
So this code actually uses LINQ to get inside a monad, like for-each gets an item from within an enumerable.
In many cases, this (the value extracted from the IEnumerable or Monad) is either their or it isn't - obviously if its there, then you can use it - if its not, nothing that would have used it ever uses it (more on this idea later)...
So this is how it works (monads generally) when using Linq syntax specifically (you'll see this is exactly like how you used it with an IEnumerable):
from a in ma
This is saying “Get the value, a out of the monad ma”. For IEnumerable that means get the values from the say List<T>,
for Option<T> it means get the value if it's not in a None state. (More on what this means later) - This is validation.
As before, the following is saying “Get the value a out of monad ma, and then if we have a value get the value b out of monad mb”. So for IEnumerable if ma is an empty collection then the second from won't run - see more about the validation in the implementation of Map and Bind()
For Option<T> if ma is a None then the second from won't run.
x = from a in ma
from b in mb
select a + b;
select in monad land, specifically above, means automatically put this value back into a new monad x
Select()and SelectMany() are extension methods that loop through the items and then put them back into the list.
So implementing a Select and a SelectMany makes it possible to use :
from a in ma
where there exists an extension method for ma called Select() as described in Understanding Select and SelectMany Implementation which is useful to look at if you dont quite understand the following conversation...
So this states that a Select invocation on an IEnumerable will run the map function on each item on the enumerable and place it back as an enumerable. In this way, map() says it will run a function on each item on the enumerable, a map function. Map() is run on every value in the enumerable
Also, a SelectMany invocation on an IEnumerable will run the bind function(which results in a IEnumerable) on each item in the list. Each item in the resulting list run against a project() function. See tutorial 01
So a bind function(for an IEnumerable) is run to transform the extracted item, and then derived from the input as each item in the IEnumerable, each item in the subsequnt list is evaluated by a project function which used the top level looping item in the enumerable that was used to produce the secondary list.
Select allows this to work:
from a in ma
select a;
SelectMany allows this to work:
from a in ma
from b in mb
select a + b;
More Articles …
Subcategories
Game Development Article Count: 28
I discovered the realms of game development purely by accident, having picked up a book entitled 'Core Techniques and Algorithms in Game Programming' and discovered a surprising niche of innovation in programming quite unparalleled to my day-to-day needs as a developer. Here optimisation, graphics rendering, and algorithms are used on a totally different level and its very interesting.
Page 5 of 17