Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IF implementations in C# [closed]

The majority of developers write IF statements in the following way

if (condition)
{
    //Do something here
}

of course this considered normal but often can create nested code which is not so elegant and a bit ugly. So the question is: Is possible to convert traditional IF statements to functional ones?

** This is an open question of possible ways to produce more readable code and i prefer not to accept any answer. I believe is better people to choose themselves the best solution for them and vote for the answer they chose.

like image 670
CodeArtist Avatar asked Oct 30 '14 06:10

CodeArtist


People also ask

What are implementations in C?

Basically, an implementation is the combination of a compiler and the C library it supports. C language = what you type into a text editor; implementation = the thing that actually does something with what you typed.

How was C implemented?

The origin of C is closely tied to the development of the Unix operating system, originally implemented in assembly language on a PDP-7 by Dennis Ritchie and Ken Thompson, incorporating several ideas from colleagues. Eventually, they decided to port the operating system to a PDP-11.

Can you do OOP in C?

In principle OOP can be done in any language, even assembly. This is because all OO language compilers/assemblers (e.g. C++) ultimately translate the high level constructs of the language into machine language.

What is the difference between C and C plus plus?

In a nutshell, the main difference between C and C++ is that C is function-driven procedural language with no support for objects and classes, whereas C++ is a combination of procedural and object-oriented programming languages.


1 Answers

Although I have already added the response about the 'functional if' aka conditional expressions. It seems something more is being asked for, and that is to facilitate a decision tree. I referred to using monads as the best approach in a reply to that. Here is how it's done. If you haven't used monads before then this might seem like voodoo, but it's essentially a better way of doing what @Gert Arnold posted.

I will take Gert's idea of a decision tree based on the suitability of a client. Instead of using the fluent style with lambdas making the decisions, I'll use LINQ. Which is C#'s native support for monads. The result of using the code will look like this:

        var client = new Client { 
            CriminalRecord = false, 
            UsesCreditCard = true, 
            YearsInJob = 10, 
            Income = 70000 
        };

        var decision = from reliable in ClientDecisions.Reliable
                       from wealthy in ClientDecisions.Wealthy
                       select "They're reliable AND wealthy";

        var result = decision(client);
        if (result.HasValue) Console.WriteLine(result.Value);

        decision = from reliableOrWealthy in Decision.Either(
                       ClientDecisions.Reliable,
                       ClientDecisions.Wealthy
                       )
                   from stable in ClientDecisions.Stable
                   select "They're reliable OR wealthy, AND stable";

        result = decision(client);
        if (result.HasValue) Console.WriteLine(result.Value);

        decision = from reliable in ClientDecisions.Reliable
                   from wealthy in ClientDecisions.Wealthy
                   from stable in ClientDecisions.Stable
                   select "They're reliable AND wealthy, AND stable";

        result = decision(client);
        if (result.HasValue) Console.WriteLine(result.Value);

The beauty of this approach is that it is fully composable. You can work with small 'decision pieces' and combine them to make larger decisions. All without any need for if.

In the above code you will see usage of a ClientDecisions static class. This is holding some re-usable decision pieces:

public static class ClientDecisions
{
    public static Decision<Client, Client> Reliable =>
        from client in Decision.Ask<Client>()
        where !client.CriminalRecord && client.UsesCreditCard
        select client;

    public static Decision<Client, Client> Wealthy =>
        from client in Decision.Ask<Client>()
        where client.Income > 100000
        select client;

    public static Decision<Client, Client> Stable =>
        from client in Decision.Ask<Client>()
        where client.YearsInJob > 2
        select client;
}

NOTE This is not using IEnumerable or IQueryable. In C# it's possible to provide Select, SelectMany and Where methods for any type and if implemented correctly allows you to make any type into a monadic type.

Interestingly in this instance I will be making a delegate into a monad. The reason for this is because we need a value passing into the computation. This is the definition of the delegate:

public delegate DecisionResult<O> Decision<I,O>(I input);

The input in the examples above is Client and the output is string. But notice that the delegate above returns a DecisionResult<O>, and not O (which would be the string). This is because we propagate through the computation a property called HasValue. If HasValue is false at any point then the computation ends. This allows us to make decisions during the computation which stops the rest of the computation from executing. This is the DecisionResult<T> class:

public struct DecisionResult<T>
{
    private readonly T value;
    public readonly bool HasValue;

    internal DecisionResult(bool hasValue, T value = default(T))
    {
        this.value = value;
        HasValue = hasValue;
    }

    public T Value =>
        HasValue
            ? value
            : throw new DecisionFailedException();
}

Next we'll add a couple of helper methods that will produce DecisionResult<T> values. They will be used in the Select, SelectMany and Where methods later.

public static class DecisionResult
{
    public static DecisionResult<O> Nothing<O>() =>
        new DecisionResult<O>(false);

    public static DecisionResult<O> Return<O>(O value) =>
        new DecisionResult<O>(true,value);
}

Now that we have the core types we can write our Select, SelectMany and Where extension methods to the Decision<I,O> delegate (Yes, you can write extension methods for delegates!)

public static class Decision
{
    public static Decision<I, V> Select<I, U, V>(this Decision<I, U> self, Func<U, V> map) =>
        input =>
        {
            var res = self(input);
            return res.HasValue
                ? DecisionResult.Return(map(res.Value))
                : DecisionResult.Nothing<V>();
        };

    public static Decision<I, V> SelectMany<I, T, U, V>(
        this Decision<I, T> self,
        Func<T, Decision<I, U>> select,
        Func<T, U, V> project) =>
        input =>
        {
            var resT = self(input);
            if (resT.HasValue)
            {
                var resU = select(resT.Value)(input);
                return resU.HasValue
                    ? DecisionResult.Return(project(resT.Value, resU.Value))
                    : DecisionResult.Nothing<V>();
            }
            else
            {
                return DecisionResult.Nothing<V>();
            }
        };

    public static Decision<I, O> Where<I, O>(this Decision<I, O> self, Predicate<O> pred) =>
        input =>
        {
            var res = self(input);
            return res.HasValue
                ? pred(res.Value)
                    ? DecisionResult.Return(res.Value)
                    : DecisionResult.Nothing<O>()
                : DecisionResult.Nothing<O>();
        };
}

These are the methods that allow Decision<I,O> to be used in a LINQ expression. We will need a couple of additional methods in the Decision class. The first is Ask<I>, it simply gets the value that was passed to the computation and allows it to be used in the expression. It's named Ask because this monad is very similar to the standard Reader monad from Haskell, and it's called ask by convention.

public static class Decision
{
    public static Decision<I,I> Ask<I>() =>
        input => DecisionResult.Return(input);
}

We also want to allow conditional operation, so we'll also add Either<I,O>(...) to Decision. This takes any number of Decision<I,O> monads, runs through them one-by-one, if one succeeds it returns the result immediately, if not it carries on. If none of the computations succeed then the whole computation will end. This allows for 'or' type behaviour, and 'switch' style behaviour:

public static class Decision
{
    public static Decision<I, O> Either<I, O>( params Decision<I, O>[] decisions ) =>
        input =>
        {
            foreach(var decision in decisions)
            {
                var res = decision(input);
                if( res.HasValue )
                {
                    return res;
                }
            }
            return DecisionResult.Nothing<O>();
        };
}

We don't need 'logical and', because it can be implemented as a series of from expressions or where expressions.

Finally we need an exception type in case the programmer tries to use .Value in DecisionResult<T> when .HasValue == false.

public class DecisionFailedException : Exception
{
    public DecisionFailedException()
        :
        base("The decision wasn't made, and therefore doesn't have a value.")
    {
    }
}

Using this technique (and building on the extension methods) you can avoid using if entirely. This is a truly functional technique. Of course it's not idiomatic, but it's the most declarative way you'll find in C#.

like image 54
louthster Avatar answered Oct 26 '22 13:10

louthster