Posts

....
Technical Blog for .NET Developers ©

Wednesday, January 12, 2022

C# & F# : Real Curry

This post is dedicated to extend a functionality coded in this previous post:

FSharp InvokeFast

Curry is a basic concept on functional programming, let's extend the defintion wth this post

Curry: Wikipedia, the free encyclopedia

In this post we code with C# Real Curry with the implementation of F#

We begin with the definition of a Curry Interface


  
    public interface ICurriedFunction<T, S, M, R>
    {
        /// <summary> Invokes the value Result
        R InvokeResult();

        /// <summary> Sets Arg1, Arg2, Arg3
        ICurriedFunction<T, S, M, R> SetArgs(Expression<Func<T>> funcArg1, Expression<Func<T, S>> funcArg2, Expression<Func<S, M>> funcArg3);

        /// <summary> Sets the Result and Curried functions
        ICurriedFunction<T, S, M, R> SetResultFunction(Converter<M, R> converter);        
    }
  
  


With this implementation



    public abstract class SpeciedFunction<R>
    {
        public abstract R InvokeResult();
    }
    
    public class CurriedFunction<T, S, M, R> : SpeciedFunction<R>, ICurriedFunction<T, S, M, R>
    {
        private FSharpFunc<T, FSharpFunc<S, FSharpFunc<M, R>>> Func { get; set; }

        private FSharpFunc<M, R> ResultFunc { get; set; }

        private FSharpFunc<S, FSharpFunc<M, R>> CurriedFunc { get; set; }

        private Expression<Func<T>> Arg1 { get; set; }

        private Expression<Func<T, S>> Arg2 { get; set; }

        private Expression<Func<S, M>> Arg3 { get; set; }

        public override R InvokeResult()
        {
            T argValue1 = this.Arg1.Compile()();

            S argValue2 = this.Arg2.Compile()(argValue1);

            M argValue3 = this.Arg3.Compile()(argValue2);

            R result = FSharpFunc<T, S>.InvokeFast<M, R>(this.Func, argValue1, argValue2, argValue3);

            return result;
        }

        public ICurriedFunction<T, S, M, R> SetResultFunction(Converter<M, R> converter)
        {
            this.ResultFunc = converter;

            this.CurriedFunc = FuncConvert.ToFSharpFunc<S, FSharpFunc<M, R>>(s => this.ResultFunc);

            this.Func = FuncConvert.ToFSharpFunc<T, FSharpFunc<S, FSharpFunc<M, R>>>(t => this.CurriedFunc);

            return this;
        }


        public ICurriedFunction<T, S, M, R> SetArgs(Expression<Func<T>> funcArg1, Expression<Func<T, S>> funcArg2, Expression<Func<S, M>> funcArg3)
        {
            this.Arg1 = funcArg1;
            this.Arg2 = funcArg2;
            this.Arg3 = funcArg3;
            return this;
        }
    }
      
      


The usability is via lambda Expressions



        void testCurried()
        {
            ICurriedFunction<Test, string, double, bool> curry = new CurriedFunction<Test, string, double, bool>();

            curry.SetResultFunction(getBool)
                 .SetArgs(() => getTest(), 
                          (s) => getTestString(s), 
                          (d) => getDouble(d));

            bool result = curry.InvokeResult();
        }       

        Test getTest() => new()
            {
                Id = 1024,
                Text = "Test Real Curry"
            };
		
        string getTestString(Test test) => (test.Id * 4000).ToString();

        double getDouble(string value) => Convert.ToDouble(value);

        bool getBool(double i) =>  i > 50;
                


METHOD SOFTWARE ® 2022